用 MelonJS 開發一個遊戲

做者:Fernando Dogliojavascript

翻譯:瘋狂的技術宅css

原文:blog.bitsrc.io/writing-a-t…html

未經容許嚴禁前端

img

遊戲開發並不須要侷限於使用 Unity 或 Unreal Engine4 的用戶。 JavaScript 遊戲開發已經有一段時間了。實際上,最流行的瀏覽器(例如Chrome,Firefox和Edge)的最新版本提供了對高級圖形渲染(例如WebGL)的支持,從而帶來了很是有趣的遊戲開發機會。java

不過用 WebGL 進行遊戲開發沒有辦法在一篇文章中涵蓋其全部內容(有專門爲此編寫的完整書籍),而且出於我的喜愛,在深刻研究特定技術以前,我更傾向於依賴框架的幫助。node

這就是爲何通過研究後,我決定用 MelonJS 編寫此快速教程的緣由。react

什麼是 MelonJS?

你可能已經猜到了,MelonJS 是一個 JavaScript 遊戲引擎,與全部主流瀏覽器徹底兼容(從 Chrome 到 Opera,一直到移動版 Chrome 和 iOS Safari)。jquery

它具備一系列功能,在個人研究過程當中很是引人注目:git

  • 對於初學者來講,它是徹底獨立的,不須要外部依賴就可使它工做。
  • 可是,它能夠與多個第三方工具集成在一塊兒,使你的工做更加輕鬆,例如Tiled(可幫助你建立地圖和遊戲關卡),TexturePacker(幫助你建立所需的紋理圖集並簡化和優化精靈管理)。
  • 集成了 2D 物理引擎。這意味着你可使用開箱即用的逼真的 2D 運動和碰撞檢測。這很關鍵,由於必須解決全部這些問題,這須要大量的工做(更不用說數學運算了,這並非個人菜)。
  • 支持聲音 API,使你可以以出色的簡便性添加聲音效果和背景音樂。

該引擎還有其餘使人讚歎的功能,你能夠在其網站上進行查看,不過以上是本文中咱們最關注的功能。github

**提示:**使用 BitGithub)能夠輕鬆共享和重用 JS 模塊,項目中的 UI 組件,建議更新。

img

Bit 組件:可以輕鬆地在團隊中跨項目共享

設計咱們的遊戲

打字遊戲的目的是經過打字(或敲擊隨機鍵)爲玩家提供移動或執行某種動做的能力。

我記得小時候曾經學過如何打字(是的,好久之前)了,當時在「Mario Teaches Typing」 這個遊戲中,必須鍵入單個字母才能前進,要麼跳到烏龜上,要麼從下面打一個方塊。下圖爲你提供了遊戲外觀以及怎樣與之進行互動的想法。

img

儘管這是一個有趣的小遊戲,但它並非一個真正的平臺遊戲,Mario 所執行的動做始終對應一個按鍵,而且永遠不會失效。

不過,對於本文,我想讓事情變得更有趣,並非建立一個簡單的打字遊戲,例如上面的遊戲:

遊戲不會經過單個字母來決定下一步的行動,而是提供了五個選擇,而且每一個選擇都必須寫一個完整的單詞:

  1. 前進
  2. 向前跳
  3. 跳起來
  4. 向後跳
  5. 向後移動

換句話說,你能夠經過輸入的單詞來移動角色,而不是經典的基於箭頭進行控制。

除此以外,該遊戲將是一個經典平臺遊戲,玩家能夠經過走動收集金幣。爲了簡潔起見,咱們會將敵人和其餘類型的實體排除在本教程以外(儘管你應該可以推斷出所使用的代碼,並能基於該代碼建立本身的實體)。

爲了使本文保持合理的長度,我將只關注一個階段,全方位的動做(換句話說,你將可以執行全部 5 個動做)、幾個敵人、一種收藏品,還有數量可觀的臺階供你跳來跳去。

你須要的工具

儘管 melonJS 是徹底獨立的,但在此過程當中有一些工具能夠幫助你們,我建議你使用它們:

  • Texture Packer:有了這個,你將可以自動生成紋理圖集,這是另外一種表達 JSON 文件的方式,其中打包了全部圖像,以便引擎之後能夠檢索並根據須要使用它們。 若是你沒有這個工具,那麼手動維護地圖集可能會消耗太多的時間。
  • Tiled:這將是咱們的關卡編輯器。儘管你能夠免費下載它(你須要找到顯示「No thanks, just take me to the downloads」 的連接),可是你能夠向該神奇工具的做者捐獻最低 1 美圓。若是你有可用的 PayPal 賬戶或借記卡,建議你這樣作,這樣的軟件須要維護,而且須要付出時間和精力。

使用這些工具,你將能夠繼續學習並完成本教程,因此讓咱們開始編碼吧。

基本的平臺遊戲

爲了開始這個項目,咱們可使用一些示例代碼。下載引擎時,它將默認附帶一組示例項目,你能夠檢出這些項目(它們位於 example 文件夾中)。

這些示例代碼是咱們用來快速啓動項目的代碼。在其中,你會發現:

  • data 文件夾,包含與代碼無關的全部內容。在這裏你能夠找到聲音、音樂、圖像、地圖定義甚至字體。

  • js文件夾,你將在這裏保存全部與遊戲相關的代碼。

  • index.html 和 index.css文件。這些是你的應用與外界互動所需的聯繫點。

瞭解現有代碼

如今暫時將資源留在 data 文件夾中,咱們須要瞭解該示例爲咱們提供了什麼。

執行遊戲

要執行遊戲,你須要作一些事情:

  1. 一份 melonJS。若是已下載,請確保得到 dist 文件夾的內容。將其複製到任意文件夾中,並確保像其餘 JS 文件同樣,將其添加到 index.html 文件中。
  2. 安裝(若是還沒有安裝)npm 中提供的 http-server 模塊,該模塊能夠快速爲相關文件夾提供 HTTP 服務。若是還沒有安裝,只需執行如下操做:
$ npm install -g http-server
複製代碼

安裝完成後,從項目文件夾中運行:

$ http-server
複製代碼

這時你能夠經過訪問 http://localhost:8080 來測試遊戲。

查看代碼

在遊戲中你會發現這是一個可以進行基本(很是尷尬)動做的平臺遊戲,幾個不一樣的敵人和一個收藏品。基本上這與咱們的目標差很少,但控制方案略有不一樣。

這裏要檢查的關鍵文件是:

  • game.js:該文件包含全部初始化代碼,有趣的是如何實例化遊戲圖形和主控件。
  • screens/play.js:包含設置關卡所需的全部代碼。你會注意到它內容並很少。因爲級別定義是使用其餘工具(即 Tiled)完成的,因此此代碼只是啓用了該功能。
  • entities/player.js:顯然這是你的主要目標。該文件包含你角色的移動代碼,碰撞反應和控制鍵綁定。雖然規模並不大,倒是你想花費最多時間的地方。
  • entities/enemies.js:僅次於 player 代碼,這很重要,由於你將看到如何基於預約義的座標來設置自動行爲。

其他文件也頗有用,但並非那麼重要,咱們會在須要時使用它們。

瞭解一切從何而來

若是你提早作好了了功課,可能已經注意到了,沒有一行實例化玩家或敵人的代碼。他們的座標無處可尋。那麼,遊戲該如何理解呢?

這是關卡編輯器所起到的做用。若是你下載了Tiled,則能夠在 data/map 文件夾中打開名爲 map1.tmx 的文件,而後會看到相似下面的內容:

img

屏幕的中心部分向你顯示正在設計的關卡。若是仔細觀察,你會看到圖像和矩形形狀,其中一些具備不一樣的顏色和名稱。這些對象表明遊戲中的 東西,具體取決於它們的名稱和所屬的層。

在屏幕的右側,你會在其中看到圖層列表(在右上方)。有不一樣類型的層:

  • 圖像層:用於背景或前景圖像
  • 對象層:用於碰撞對象、實體以及你想在地圖中實例化的任何對象。
  • Tile 層:你將在其中放置 Tile 以建立實際關卡的位置。

右下角包含此地圖的圖塊。 tileet 也能夠由 Tiled 建立,而且能夠在同一文件夾中以 tsx 擴展名找到該 tileet。

最後,在屏幕左側,你會看到「屬性」部分,在這裏你將看到有關所選對象或單擊的圖層的詳細信息。你將可以更改通用屬性(例如圖層的顏色,以便更好地瞭解其對象的位置)並添加自定義屬性(稍後將其做爲參數傳遞給遊戲中實體的構造函數)。

更改運動方案

如今咱們已經準備好進行編碼了,讓咱們專一於本文的主要目的,咱們將以示例的工做版本爲例,嘗試對其進行修改,使其能夠用做打字遊戲。

這意味着,須要更改的第一件事是運動方案,或者換句話說:更改控制。

轉到 entities/player.js 並檢查 init 方法。你會注意到不少 bindKeybindGamepad 調用。這些代碼本質上是將特定按鍵與邏輯操做綁定在一塊兒。簡而言之,它能夠確保不管你是按向右箭頭鍵,D 鍵仍是向右移動模擬搖桿,都會在代碼中觸發相同的「向右」動做。

全部這些都須要將其刪除,這對咱們沒什麼用。同時建立一個新文件,將其命名爲 wordServices.js,並在此文件中建立一個對象,該對象將在每一個回合中返回單詞,這可以幫助咱們瞭解玩家到底選擇了哪一個動做。

/** * Shuffles array in place. * @param {Array} a items An array containing the items. */
function shuffle(a) {
    var j, x, i;
    for (i = a.length - 1; i > 0; i--) {
        j = Math.floor(Math.random() * (i + 1));
        x = a[i];
        a[i] = a[j];
        a[j] = x;
    }
    return a;
}


ActionWordsService = {

    init: function(totalActions) {
        //load words...
        this.words = [
            "test", "hello", "auto", "bye", "mother", "son", "yellow", "perfect", "game"
        ]
        this.totalActions = totalActions
        this.currentWordSet = []
    },

    reshuffle: function() {
        this.words = shuffle(this.words)
    },

    getRegionPostfix: function(word) {
        let ws = this.currentWordSet.find( ws => {
            return ws.word == word
        })
        if(ws) return ws.regionPostfix
        return false
    },

    getAction: function(word) {
        let match = this.getWords().find( am => {
            return am.word == word
        })
        if(match) return match.action
        return false
    },

    getWords: function() {
        let actions = [ { action: "right", coords: [1, 0], regionPostfix: "right"}, 
                        { action: "left", coords: [-1, 0], regionPostfix: "left"}, 
                        { action: "jump-ahead", coords: [1,-0.5], regionPostfix: "upper-right"}, 
                        { action: "jump-back", coords:[-1, -0.5], regionPostfix: "upper-left"},
                        { action: "up", coords: [0, -1], regionPostfix: "up"}
                    ]

       this.currentWordSet = this.words.slice(0, this.totalActions).map( w => {
            let obj = actions.shift()
            obj.word = w
            return obj
       })
       return this.currentWordSet
    }
}
複製代碼

本質上,該服務包含一個單詞列表,而後將其隨機排列,而且每次請求該列表時(使用 getWords 方法),都會隨機獲取一組單詞,並將它們分配給上面提到的一種操做。還有與每一個操做相關的其餘屬性:

  • 基於動做 HUD,coords 屬性用於將文本放置在正確的座標中(稍後會詳細介紹)
  • regionPostfix 屬性用於爲 HUD 操做選擇正確的框架。

如今,讓咱們看看如何在遊戲過程當中請求用戶輸入。

注意:繼續前進以前,請記住,爲了使新服務可用於其他代碼,你必須將其包含在 index.html 文件中,就像其餘 JS 庫同樣:

<script type="text/javascript" src="js/wordServices.js"></script>
複製代碼

如何捕獲用戶輸入

你能夠潛在地使用鍵綁定的組合來模仿使用遊戲元素的輸入字段的行爲,可是請考慮輸入字段默認提供的全部可能的組合和行爲(例如,粘貼文本、選擇、移動而不刪除字符等) ),必須對全部程序進行編程以使其可用。

相反,咱們能夠簡單地在 HTML 主頁面中添加一個文本字段,並使用 CSS 對其進行樣式設置,使其位於 Canvas 元素之上,它將成爲遊戲的一部分。

你只須要在 <body> 內的這段代碼便可:

<input type="text" id="current-word" />
複製代碼

儘管這徹底取決於你,但我仍是建議你使用 jQuery 來簡化將回調附加到 keypress 事件上所需的代碼。固然可使用原生 JS 完成此操做,但我更喜歡這個庫提供的語法糖。

如下代碼位於 game.js 文件的 load 方法中,負責捕獲用戶的輸入:

me.$input = $("#current-word")

let lastWord = ''
me.$input.keydown( (evnt) => {

    if(evnt.which == 13) {
        console.log("Last word: ", lastWord)
        StateManager.set("lastWord", lastWord)
        lastWord = ''
        me.$input.val("")
    } else {
        if(evnt.which > 20) {
            let validChars = /[a-z0-9]+/gi
            if(!String.fromCharCode(evnt.which).match(validChars)) return false
          }

        setTimeout(_ => {
            lastWord = me.$input.val() //String.fromCharCode(evnt.which)
            console.log("Partial: ", lastWord)
        }, 1)
    }
    setTimeout(() => {
        StateManager.set("partialWord", me.$input.val())
    }, 1);
})
複製代碼

本質上是咱們捕獲輸入元素並將其存儲在全局對象 me 中。這個全局變量包含遊戲所需的一切。

這樣,咱們能夠爲按下的任何按鍵設置事件處理程序。如你所見,我正在檢查鍵碼 13(表明ENTER鍵)以識別玩傢什麼時候完成輸入,不然我將確保他們輸入的是有效字符(我只是避免使用特殊字符,這樣能夠防止 melonJS 提供的默認字體出現問題)。

最後我在 StateManager 對象上設置了兩個不一樣的狀態,lastWord 瞭解玩家輸入的最後一個單詞,partialWord 解如今正在輸入的內容。這兩種狀態很重要。

組件之間共享數據

如何在組件之間共享數據是不少框架中的常見問題。咱們將捕獲的輸入做爲 game 組件的一部分,那麼該如何與他人共享這個輸入呢?

個人解決方案是建立一個充當事件發送器(event emitter)的全局組件:

const StateManager = {

    on: function(k, cb) {
        console.log("Adding observer for: ", k)
        if(!this.observers) {
            this.observers = {}
        }

        if(!this.observers[k]) {
            this.observers[k] = []
        }
        this.observers[k].push(cb)
    },
    clearObserver: function(k) {
        console.log("Removing observers for: ", k)
        this.observers[k] = []
    },
    trigger: function(k) {
        this.observers[k].forEach( cb => {
            cb(this.get(k))
        })
    },
    set: function(k, v) {
        this[k] = v
        this.trigger(k)
    },
    get: function(k) {
        return this[k]
    }

}
複製代碼

代碼很是簡單,你能夠爲特定狀態設置多個「觀察者」(它們是回調函數),而且一旦設置了該狀態(即更改),便會用新值調用全部這些回調。

添加 UI

建立關卡以前的最後一步是顯示一些基本的 UI。由於咱們須要顯示玩家能夠移動的方向以及須要輸入的單詞。

爲此將使用兩個不一樣的UI元素:

  • 一個用於圖形,它將具備幾個不一樣的幀,本質上一個用於正常圖像,而後一個將每一個方向顯示爲「selected」(與 ActionWordsService 上的 regionPostfix 屬性相關聯)
  • 一個用於在圖像周圍輸出文本。順便說一下,這也與 ActionWordsService 上的 coords 屬性相關聯。

咱們能夠在 js 文件夾內搭上現有的 HUD.js 文件。在其中添加兩個新組件。

第一個是 ActionControl 組件,以下所示:

game.HUD.ActionControl = me.GUI_Object.extend({
    init: function(x, y, settings) {
        game.HUD.actionControlCoords.x = x //me.game.viewport.width - (me.game.viewport.width / 2)
        game.HUD.actionControlCoords.y = me.game.viewport.height - (me.game.viewport.height / 2) + y

        settings.image = game.texture;

        this._super(me.GUI_Object, "init", [
            game.HUD.actionControlCoords.x, 
            game.HUD.actionControlCoords.y, 
            settings
        ])

        //update the selected word as we type
        StateManager.on('partialWord', w => {
            let postfix = ActionWordsService.getRegionPostfix(w)
            if(postfix) {
                this.setRegion(game.texture.getRegion("action-wheel-" + postfix))
            } else {
                this.setRegion(game.texture.getRegion("action-wheel")
            }
            this.anchorPoint.set(0.5,1)
        })

        //react to the final word
        StateManager.on('lastWord', w => {
            let act = ActionWordsService.getAction(w)
            if(!act) {

                me.audio.play("error", false);
                me.game.viewport.shake(100, 200, me.game.viewport.AXIS.X)
                me.game.viewport.fadeOut("#f00", 150, function(){})
           } else {
               game.data.score += Constants.SCORES.CORRECT_WORD
           }
        })
    }
})
複製代碼

看起來不少,可是它只是作了一點事情:

  1. 它從 settings 屬性中提取其座標,在 Tiled 上設置地圖後,咱們將對其進行檢查。
  2. 添加對輸入了一部分的單詞做出反應的代碼。咱們將 postfix 屬性用於當前編寫的單詞。
  3. 並添加了對完整的詞作出反應的代碼。若是某個動做與該字詞相關聯(便是正確的詞),那麼它將爲玩家加分。不然將晃動屏幕並播放錯誤聲音。

第二個圖形部分,即要輸入的單詞,以下所示:

game.HUD.ActionWords = me.Renderable.extend({
    init: function(x, y) {
        this.relative = new me.Vector2d(x, y);

        this._super(me.Renderable, "init", [
            me.game.viewport.width + x,
            me.game.viewport.height + y,
            10, //x & y coordinates
            10
        ]);

         // Use screen coordinates
        this.floating = true;

        // make sure our object is always draw first
        this.z = Infinity;
        // create a font
        this.font = new me.BitmapText(0, 0, {
            font : "PressStart2P",
            size: 0.5,
            textAlign : "right",
            textBaseline : "bottom"
        });

        // recalculate the object position if the canvas is resize
        me.event.subscribe(me.event.CANVAS_ONRESIZE, (function(w, h){
            this.pos.set(w, h, 0).add(this.relative);
        }).bind(this));

        this.actionMapping = ActionWordsService.getWords()
    },

    update: function() {
        this.actionMapping = ActionWordsService.getWords()
        return true
    },
    draw: function(renderer) {
        this.actionMapping.forEach( am => {
            if(am.coords[0] == 0 && am.coords[1] == 1) return 
            let x = game.HUD.actionControlCoords.x + (am.coords[0]*80) + 30
            let y = game.HUD.actionControlCoords.y + (am.coords[1]*80) - 30
            this.font.draw(renderer, am.word, x, y)
        })
    }
})
複製代碼

該組件的繁重工做是經過 draw 方法完成的。 init 方法只是初始化變量。在調用 draw 的過程當中,咱們將迭代選定的單詞,並使用與之相關的座標以及一組固定數字,將單詞定位在 ActionControl 組件的座標周圍。

這是建議的動做控制設計的樣子(以及座標如何與之關聯):

img

固然,它應該有透明的背景。

只需確保將這些圖像保存在 /data/img/assets/UI 文件夾中,這樣當你打開 TexturePacker 時,它將識別出新圖像並將其添加到紋理中地圖集。

img

上圖顯示瞭如何添加 action wheel 的新圖像。而後,你能夠單擊「Publish sprite sheet」並接受全部默認選項。它將覆蓋現有的地圖集,所以對於你的代碼無需執行任何操做。這一步驟相當重要,由於紋理地圖集將做爲資源加載(一分鐘內會詳細介紹),而且多個實體會將其用於動畫之類的東西。請記住,在遊戲上添加或更新圖形時,都務必這樣作。

將全部內容與Tiled放在一塊兒

好了,如今咱們已經介紹了基礎知識,讓咱們一塊兒玩遊戲。首先要注意的是:地圖。

經過使用 tiled 和 melonJS 中包含的默認 tileet,我建立了這個地圖( 25x16 tiles 地圖,其中 tile 爲 32 x 32px):

img

這些是我正在使用的圖層:

  • HUD:它僅包含一個名爲 HUD.ActionControl 的元素(重要的是要保持名稱相同,一下子你會明白爲何)。下圖顯示了此元素的屬性(請注意自定義屬性)

img

  • collision:默認狀況下,melonJS 會把以 collision 開頭的全部層都假定爲碰撞層,這意味着其中的任何形狀都是不可遍歷的。在這裏你將定義地板和平臺的全部形狀。
  • player:該層僅包含 mainPlayer 元素(一種形狀,該形狀將使 melonJS 知道在遊戲開始時須要放置玩家的位置)。
  • entities:在這一層中,我再次添加了硬幣,它們的名稱很重要,請保持一致,由於它們須要與你在代碼中註冊的名稱相匹配。
  • 最後三層就能夠在其中添加地圖和背景的圖像。

準備好以後,咱們能夠轉到 game.js 文件,並在 loaded 方法內添加如下幾行:

// register our objects entity in the object pool
me.pool.register("mainPlayer", game.PlayerEntity);
me.pool.register("CoinEntity", game.CoinEntity);
me.pool.register("HUD.ActionControl", game.HUD.ActionControl);
複製代碼

這些代碼用來註冊你的實體(你要使用 Tiled 直接放置在地圖上的實體)。第一個參數提供的名稱是你須要用 Tiled 進行匹配的名稱。

此外,在此文件中,onLoad 方法應以下所示:

onload: function() {

        // init the video
        if (!me.video.init(965, 512, {wrapper : "screen", scale : "auto", scaleMethod : "fit", renderer : me.video.AUTO, subPixel : false })) {
            alert("Your browser does not support HTML5 canvas.");
            return;
        }

        // initialize the "sound engine"
        me.audio.init("mp3,ogg");

        // set all ressources to be loaded
        me.loader.preload(game.resources, this.loaded.bind(this));
        ActionWordsService.init(5)
    },
複製代碼

咱們的基本要求是 965x512 的分辨率(我發現,當屏幕的高度與地圖的高度相同時效果很好。在咱們的例子中爲 16*32 = 512)以後,將使用5個單詞(這些是你能夠繼續前進的5個方向)初始化 ActionWordsService

onLoad 方法中另外一條有趣的代碼是:

me.loader.preload(game.resources, this.loaded.bind(this));
複製代碼

資源文件

遊戲須要的全部類型的資源(即圖像、聲音、背景音樂、JSON 配置文件等)都須要添加到 resources.js 文件中。

這是你資源文件的內容:

game.resources = [

    { name: "tileset",     type:"image", src: "data/img/tileset.png" },
    { name: "background",  type:"image", src: "data/img/background.png" },
    { name: "clouds",      type:"image", src: "data/img/clouds.png" },

    
    { name: "screen01", type: "tmx", src: "data/map/screen01.tmx" },

    { name: "tileset",  type: "tsx", src: "data/map/tileset.json" },

    { name: "action-wheel", type:"image", src: "data/img/assets/UI/action-wheel.png" },
    { name: "action-wheel-right", type:"image", src: "data/img/assets/UI/action-wheel-right.png" },
    { name: "action-wheel-upper-right",type:"image", src: "data/img/assets/UI/action-wheel-upper-right.png" },
    { name: "action-wheel-up", type:"image", src: "data/img/assets/UI/action-wheel-up.png" },
    { name: "action-wheel-upper-left", type:"image", src: "data/img/assets/UI/action-wheel-upper-left.png" },
    { name: "action-wheel-left", type:"image", src: "data/img/assets/UI/action-wheel-left.png" },

    { name: "dst-gameforest", type: "audio", src: "data/bgm/" },

    { name: "cling",     type: "audio", src: "data/sfx/" },
    { name: "die",       type: "audio", src: "data/sfx/" },
    { name: "enemykill", type: "audio", src: "data/sfx/" },
    { name: "jump",      type: "audio", src: "data/sfx/" },

    { name: "texture",   type: "json",  src: "data/img/texture.json" },
    { name: "texture",   type: "image", src: "data/img/texture.png" },

    { name: "PressStart2P", type:"image", src: "data/fnt/PressStart2P.png" },
    { name: "PressStart2P", type:"binary", src: "data/fnt/PressStart2P.fnt"}
];
複製代碼

其中你可使用諸如圖塊集、屏幕映射之類的東西(請注意,名稱始終是不帶擴展名的文件名,這是強制性的要求,不然將找不到資源)。

硬幣

遊戲中的硬幣很是簡單,可是當你與它們碰撞時,須要發生一些事情,它們的代碼以下所示:

game.CoinEntity = me.CollectableEntity.extend({

    /** * constructor */
    init: function (x, y, settings) {
        // call the super constructor
        this._super(me.CollectableEntity, "init", [
            x, y ,
            Object.assign({
                image: game.texture,
                region : "coin.png"
            }, settings)
        ]);

    },

    /** * collision handling */
    onCollision : function (/*response*/) {

        // do something when collide
        me.audio.play("cling", false);
        // give some score
        game.data.score += Constants.SCORES.COIN

        //avoid further collision and delete it
        this.body.setCollisionMask(me.collision.types.NO_OBJECT);

        me.game.world.removeChild(this);

        return false;
    }
});
複製代碼

請注意,硬幣實體其實是擴展了 CollectibleEntity (這給它提供了一個特殊的衝撞類型給實體,所以melonJS知道在玩家移過它時會調用碰撞處理程序),你要作的就是調用其父級的構造函數,而後當你拾起它時,在 onCollision 方法上會播放聲音,在全局得分中加 1,最後從世界中刪除對象。

成品

將全部內容放在一塊兒,就有了一個能夠正常工做的遊戲,該遊戲可讓你根據輸入的單詞在 5 個不一樣的方向上移動。

它看起來應該像這樣:

img

而且因爲本教程已經太長了,你能夠在 Github 上查看該遊戲的完整代碼。

歡迎關注前端公衆號:前端先鋒,領取前端工程化實用工具包。

相關文章
相關標籤/搜索