Quick-Cocos2d-x初學者游戲教程(六)
上一章我 們介紹了開發中會用到的輔助工具,並創建了 GameScene 場景,接下來這章我們將繼續 GameScene 的傳(bai)奇(bi)。不過在開始編寫 GameScene 場景的代碼之前,我們還是先來明確一下游戲的功能和實現方法。這樣可以幫我們更好的理解並設計邏輯。下面是總結出的結論:
- 在 GameScene 場景中,我們將創建一個飛行的娃娃角色,這個角色是游戲的唯一主角。游戲初始狀態下,這個角色有滿滿的生命值,但隨着時間的推移,生命值會不斷的減少。這里生命值我們可以以進度條的形式來展示它的多少和增減。
- 游戲 GameScene 場景有不止一層的滾動背景,每層的背景滾動速率不一,這樣可以產生立體的滾動效果。娃娃位置不動,讓背景不停的滾動,可以產生娃娃在向前飛行的視覺感受。
- 在滾動過程中屏幕上會時不時的出現一些障礙物和可以增加生命值的獎勵品,當娃娃碰到障礙物時,生命值減少;碰到獎勵品時,生命值增加。
- 當觸碰屏幕時,娃娃將向上升至一定的高度;當不觸碰屏幕時,它將向下掉落。掉落到地板時會扣除相應的生命值。所以玩家必須在按與不按之間保持一定的平衡,一方面躲避障礙物並避免觸碰地板,另一方面爭取多吃一點獎勵品,這樣才能讓娃娃順利地到達終點。
- 本場景我們將用物理引擎來模擬整個飛行世界,這樣游戲主角的飛行問題(漂浮狀態),以及它與障礙物/獎勵品之間的碰撞檢測就很容易實現了。
GameScene 的背景
為了產生立體的滾動視覺效果,我們決定用四層不同的背景來實現這一效果(怕累死的,可以偷懶)。當然,這里層的概念不同於引擎中的層(cc.Layer),它只是一個普通的副詞而已。
- 首先,最底層是一張固定不動的布幕背景;
- 其次,我將在布幕背景的上層添加一層遠景背景,該背景將會以較慢的速度向左滾動;
- 然后,在遠景背景的上層我們又會添加一層近景背景,該背景將會以較快的速度向左滾動;
- 最后,在最上層,我們將放置一層以更快速度向左滾動的背景,游戲的障礙物和獎勵品都會在該層背景上。所以這層背景我們將用 TliedMap 編輯器來制作,也就是用 TMX 文件來創建。
亂入一個知識點:游戲中元素的層級關系由 z-order 屬性來決定,我們可以通過設置元素的z-order值來控制元素之間的渲染順序。默認情況下,所有元素的 z-order 為0,所以當游戲元素沒有指定z-order值時,游戲中的元素將按添加順序來顯示。故此,我們在添加游戲背景或其他元素時,應該要注意下它們的添加順序 或 z-order 值,不要出現遮擋的現象。
創建BackgroundLayer背景層
為了讓代碼結構更加清晰,接下來我們將為 GameScene 場景創建一個背景層,然后把以上的四層背景圖都添加到該層上。
所以,我們在 src/app 目錄下新建了一個layers 文件夾,然后再新建了一個 BackgroundLayer.lua 文件,並把它保存到 src/app/layers 目錄下。
以下是創建空白 BackgroundLayer 層的代碼:
1
2
3
4
5
6
7
8
|
BackgroundLayer =
class
(
"BackgroundLayer"
,function()
return
display.newLayer()
end)
function BackgroundLayer:ctor()
end
return
BackgroundLayer
|
display.newLayer()方法能創建並返回一個 cc.Layer 層對象。
注意:因為我們將在其他(GameScene.lua)文件中調用 BackgroundLayer 類,所以在創建 BackgroundLayer 類時,我們並沒有像創建 GameScene 一樣把它定義為 local 局部型的類。
把背景層添加到GameScene場景
BackgroundLayer.lua 文件是個單獨的模塊文件,如果我們想要引用它,那我們需要把它加載到項目中來。
通常,載入文件到 Quick 項目可以使用以下的兩種方式:
- 通過require()方法,該方法會搜索指定的目錄,並加載文件。
- 通過import()方法,該方法用於處理require同目錄下其他模塊,在模塊名前加.。
它們的詳細用法參見API。
這里,我們載入 BackgroundLayer 模塊用 require 方法。在 MyApp.lua 中加入如下的函數:
1
|
require(
"app.layers.BackgroundLayer"
)
|
這樣就把我們定義的 BackgroundLayer 類引入到了quick項目中,之后,我們就可以在任何地方引用這個 BackgroundLayer 模塊了。
接下來我們來把 BackgroundLayer 層加入到 GameScene 場景中。
1
2
3
4
5
|
function GameScene:ctor()
self.backgroundLayer = BackgroundLayer.
new
()
:addTo(self)
end
|
這里調用BackgroundLayer.new()
方法實例化了一個 BackgroundLayer 對象,並把它加入到場景。這樣 GameScene 場景中就有一層空白的 BackgroundLayer 層了。
框架整好以后,下面我們來向層容器里面塞東西。
添加背景
布幕背景
回到 BackgroundLayer 文件,下面我們添加如下的一段函數來為 BackgroundLayer 層添加最底層的布幕背景,這里添加固定不動的布幕背景就如添加普通精靈。
1
2
3
4
5
6
|
function BackgroundLayer:createBackgrounds()
-- 創建布幕背景
local bg = display.newSprite(
"image/bj1.jpg"
)
:pos(display.cx, display.cy)
:addTo(self, -4)
end
|
addTo方法中可以指定游戲元素的 z-order 值,本教程中,我們把布幕背景的z-order設置為-4,確保它位於場景的最下層(當然,這要確保該層中不能有比布幕背景的 z-order 值還小的元素)。
循環滾動的遠/近景背景
遠景背景和近景背景有着共同的特征,它們都會以一定的速度向左循環移動。所以這里我們以遠景背景為例,說明下它們的實現過程。
首先,我們添加兩張首尾可以拼接的圖片來表示滾動的遠景背景圖。
在BackgroundLayer:createBackgrounds()
方法中加入如下的代碼來添加遠景背景圖:
1
2
3
4
5
6
7
8
9
10
|
-- 創建遠景背景
local bg1 = display.newSprite(
"image/b2.png"
)
:align(display.BOTTOM_LEFT, display.left , display.bottom + 10)
:addTo(self, -3)
local bg2 = display.newSprite(
"image/b2.png"
)
:align(display.BOTTOM_LEFT, display.left + bg1:getContentSize().width, display.bottom + 10)
:addTo(self, -3)
table.insert(self.distanceBg, bg1) -- 把創建的bg1插入到了 self.distanceBg 中
table.insert(self.distanceBg, bg2) -- 把創建的bg2插入到了 self.distanceBg 中
|
其中 self.distanceBg 是一個 table 類型的值,它的定義我們放在 ctor 函數中。
1
|
self.distanceBg = {}
|
實現滾動背景,需要做的就是不斷的改變背景圖片貼圖橫坐標,並且不斷的刷新位置。所以我們定義了一個滾動背景的函數scrollBackgrounds()。
1
2
3
4
5
6
7
8
9
10
11
12
|
function BackgroundLayer:scrollBackgrounds(dt)
if
self.distanceBg[2]:getPositionX() <= 0 then
self.distanceBg[1]:setPositionX(0)
end
local x1 = self.distanceBg[1]:getPositionX() - 50*dt -- 50*dt 相當於速度
local x2 = x1 + self.distanceBg[1]:getContentSize().width
self.distanceBg[1]:setPositionX(x1)
self.distanceBg[2]:setPositionX(x2)
end
|
以上的這段函數的作用就是讓 self.distanceBg[1]
和 self.distanceBg[2]
的 X 坐標都向左移動 50 * dt (dt是時間間隔,兩幀之間的時間間隔)個單位,self.distanceBg[2]
緊接在 self.distanceBg[1]
后面。
在此之后,需要添加不斷執行 scrollBackgrounds() 函數的方法,以確保遠景背景不斷的向左移動。使用過 Cocos2d-x 的童鞋應該知道,Cocos2d-x 中可以通過重載 update 函數在每幀刷新的時候執行自己需要的一些操作。在 Quick 框架中,我們把這種事件叫做幀事件,意思是每幀刷新時都會執行的事件。
幀事件在游戲中經常用來更新游戲中的數據。下面我們將在 ctor() 函數中加入這種幀事件,用以更新背景圖的坐標。代碼如下:
1
2
3
4
5
6
7
8
9
10
11
|
function BackgroundLayer:ctor()
self.distanceBg = {}
self.nearbyBg = {}
self.tiledMapBg = {}
self:createBackgrounds()
self:addNodeEventListener(cc.NODE_ENTER_FRAME_EVENT, handler(self, self.scrollBackgrounds))
self:scheduleUpdate()
end
|
其中,addNodeEventListener 方法用於注冊幀事件,scheduleUpdate 方法則啟用了幀事件,只有調用了 scheduleUpdate 后,幀事件才會被觸發。
此時我們再用同樣的方法添加近景背景(讓它每次移動的距離大一些),運行游戲時,屏幕上就會出現滾循環移動的背景了。
注:以上截圖不是循環的。
最上層的TMX背景
背景層中還有一個重要的滾動項,那就是容納了障礙物和獎勵品的 TMX 類型的背景。
前面章節中我們已經提到過,TiledMap 編輯器能把單個的圖塊拼接成一幅完整的地圖,而它的最終產物就是 TMX 文件。這里我們也不要把它想的有多復雜,其實說白了,渲染出來就是一張破圖而已。和 png,jpg的圖片精靈外形無明顯差異。
所以它滾動的原理和遠/近景背景滾動的原理差不多,只不過,它不循環。我們可以用一個以上的 TMX 文件來實現滾動,當最后一個 TMX 文件剛好顯示完的時候游戲就結束。這里考慮到后續的碰撞檢測,所以我們只用一個 TMX 文件實現滾動。
載入 TMX 文件的代碼如下,添加的位置依舊在 createBackgrounds 方法中。
1
2
3
|
self.map = cc.TMXTiledMap:create(
"image/map.tmx"
)
:align(display.BOTTOM_LEFT, display.left, display.bottom)
:addTo(self, -1)
|
讓地圖文件滾動的代碼如下:
1
2
3
4
5
6
|
if
self.map:getPositionX() <= display.width - self.map:getContentSize().width then
self:unscheduleUpdate() -- 禁用幀事件,停止整個背景層滾動
end
local x5 = self.map:getPositionX() - 130*dt
self.map:setPositionX(x5)
|
好了,本周的教程就算完成了,這里我們的 tmx 文件是在下暫時隨便創建的一個,下一章我們會詳細地講解如何制作 tmx 文件。
另外,關於本游戲的資源我們將會在下章好好制作 tmx 文件后一並上傳。