關卡 (levels)
關卡是透過 Level.lua
管理。 Level.lua
會根據 gameConfig.gameLevels
調整關卡內容。關卡分類如下:
魔王關卡 (Boss Level)
被標記為 Boss Level 的關卡,開始前會先出現警告。Boss Level 會有一個魔王 (Boss),在無限模式中,玩家必須要打倒魔王才能繼續前進。
普通關卡 (Normal Level)
除了 Boss Level 外的關卡皆為 Normal Level,Normal Level 可以有很多種變化,像是消滅一定數量的敵人、解救人質、閃避隕石等等。
新增一般關卡
新增一般關卡的方式非常簡單:
新增關卡檔:
關卡必需繼承 Sublevel.lua
,這裡提供一個簡單的範例:level_simplest_example.lua
,示範如何用最少的程式碼實作一個關卡。這個例子裡,我們指定 duration
為 30000 毫秒(30秒),這代表如果玩家在此關卡開始後, 過了 30 秒依然存活的話,便會結束目前關卡,接著進行下一關。也由於沒有新增任何遊戲物件的關係,遊戲開始後的畫面只會出現玩家。9999999-015
是關卡的 id,每個關卡都必須有一個獨一無二的 level id,這會被用來區分每個關卡單獨執行時的得分。由於 level id 最長 20 碼,而且必須獨一無二。
--levels.myLevel.level_simplest_example.lua
local Sublevel = require("Sublevel")
local myLevel = Sublevel.new("9999999-015", "level name", "author name", {duration = 30000})
function myLevel:show(options)
end
return myLevel
當關卡檔案完成後,我們必須要將它放在 levels 資料夾裡。關於放置的方法:你可以直接將檔案放進去,這時候關卡路徑會是 levels/level_simplest_example.lua:
.
├── levels
│ ├── default
│ │ ├── level_1.lua
│ │ ├── level_2.lua
│ │ ├── level_3.lua
│ │ ├── level_4.lua
│ │ ├── level_5.lua
│ │ ├── level_6.lua
│ │ ├── level_bonus.lua
│ │ ├── level_boss_1.lua
│ │ └── level_test.lua
│ ├── level_bossfight_warning.lua
│ └── level_simplest_example.lua
或是放進建立的子目錄內:levels/myLevel/level_simplest_example.lua。這邊為了方便管理,採用的是後者的方法。
.
├── levels
│ ├── default
│ │ ├── level_1.lua
│ │ ├── level_2.lua
│ │ ├── level_3.lua
│ │ ├── level_4.lua
│ │ ├── level_5.lua
│ │ ├── level_6.lua
│ │ ├── level_bonus.lua
│ │ ├── level_boss_1.lua
│ │ └── level_test.lua
│ ├── level_bossfight_warning.lua
│ └── myLevel
│ └── level_simplest_example.lua
設定 gameConfig.lua:
無限模式設定方式
不論你選擇如何放置你的關卡檔案,都要在 gameLevels 設定關卡位置,這樣一來模板才能順利將指定的關卡顯示出來:
預設:
config.gameLevels = {
"default.level_boss_1",
"default.level_1",
"default.level_2",
"default.level_3",
"default.level_4",
"default.level_5",
"default.level_bonus"
}
修改後:
config.gameLevels = {
"default.level_boss_1",
"default.level_1",
"default.level_2",
"default.level_3",
"default.level_4",
"default.level_5",
"default.level_bonus",
"myLevel.level_simplest_example" --added level
}
修改之後,遊戲變便會隨機執行設定檔內的這 9 個關卡檔案,當然為了方便測試,你也可以只留下你所以設計的關卡,這樣遊戲便會重複執行同一關:
config.gameLevels = {
"myLevel.level_simplest_example" --added level
}
在關卡內新增敵人
這裡我們利用模板中已經建立好的遊戲物件:Enemy.EnemyPlane
,來示範如何用最少的程式碼在關卡內新增敵人。
-- levels/myLevel/level_single_enemy.lua
local gameConfig = require("gameConfig")
local Sublevel = require("Sublevel")
local EnemyPlane = require("enemies.EnemyPlane")
local myLevel = Sublevel.new("9999999-016", "level name", "author name", {duration = 4000})
function myLevel:show(options)
local enemy = EnemyPlane.new()
self:insert(enemy)
--place the enemy out of the screen
enemy.x = gameConfig.contentWidth/4
enemy.y = -100
--move the enemy from the top to bottom with speed 100 pixels/second
enemy:setLinearVelocity( 0, 100 )
--destroy the enemy properly
enemy:autoDestroyWhenInTheScreen()
end
return myLevel
這個關卡會出現一架由上往下飛行的飛機。首先我們先新增敵人物件(第 8 行),並記得透過方法 insert 加入關卡之中。加入關卡的物件才能順利地被遊戲功能所套用,像是暫停、恢復、重新開始。
注意遊戲物件在關卡結束前必須適當的回收,否則遊戲會因為不斷增加的物件而崩潰。所以這裡我們調用方法 autoDestroyWhenInTheScreen
,會在物件進入遊戲畫面時,開始自動回收,所謂的自動回收是指:當物件離開遊戲畫面時,會被系統所回收。
如果你希望你的遊戲物件可以反覆的進出遊戲畫面,那麼 autoDestroyWhenInTheScreen
就不適合,因為你會發現當你的遊戲物件離開遊戲畫面後,它就會消失了。這時候你必須自己呼叫遊戲物件中的方法 clear()
來回收。
自訂關卡結束條件
要自訂關卡結束的條件,你必須先移除 duration
設定,並且覆寫判斷關卡結束的方法 Sublevel:isFinish()
。關卡管理器會在每幀檢查這個方法的回傳值,如果回傳的是 True
,則會結束此關,反之則會繼續讓關卡執行。注意,所謂的結束關卡不代表遊戲會自動回收目前遊戲畫面上的所有物件,而是會接著進行下一關。你必須善盡回收遊戲物件的責任。
--levels/myLevel/level_custom_finish.lua
local gameConfig = require("gameConfig")
local Sublevel = require("Sublevel")
local EnemyPlane = require("enemies.EnemyPlane")
local util = require("util")
local myLevel = Sublevel.new("9999999-012", "level name", "author name")
function myLevel:show(options)
local enemy = EnemyPlane.new()
self:insert(enemy)
--place the enemy out of the screen
enemy.x = gameConfig.contentWidth/4
enemy.y = -100
--move the enemy from the top to bottom with speed 100 pixels/second
enemy:setLinearVelocity( 0, 100 )
--destroy the enemy properly
enemy:autoDestroyWhenInTheScreen()
self.enemy = enemy
end
function myLevel:isFinish()
if util.isExists(self.enemy) then
return false
else
return true
end
end
return myLevel
以上的例子使用 util.isExist
判斷遊戲物件是否消失,如果物件消失了,便結束關卡。而且為了讓 myLevel:isFinish()
能順利取得 enemy 物件,我們在第 17 行將它設定為此關卡的屬性。
動態關卡 (Dynamic Level)
在這個遊戲模板中,遊戲模式可以分為無限模式 (Infinite Mode) 與單一關卡模式 (Single Level Mode) ,無限模式如之前所提,開始遊玩後關卡間會不斷循環,直到玩家死亡。而在單一關卡模式中 (如下圖) 玩家可以從多個關卡中選擇一關遊玩。
單一關卡的關卡檔案與無限模式並無不同,但是必須在 gameConfig.lua
中的 config.seperateLevels
註冊:
config.seperateLevels = {
"default.level_1",
"default.level_2",
"default.level_3",
"default.level_4",
"default.level_5",
"myLevel.level_dynamic",
}
那麼什麼是動態關卡呢?動態關卡的意思即是:將同一個關卡檔案運用在不同的遊戲模式中。
你可以透過 sublevel 中的 gameMode
屬性取得當前的遊戲模式,如果gameMode
為 GameConfig.MODE_SINGLE_LEVEL
則代表玩家目前正在遊玩單一關卡模式,如果是 GameConfig.MODE_INFINITE_LEVEL
則代表目前正在遊玩無限模式。
下面的例子即是透過這樣的技巧,讓同一個關卡的敵人在不同模式中有不同的行為,在單一關卡模式中,該敵人會往下飛行,而在無限模式中,則會往右下方飛行:
--/levels/myLevel/level_dynamic.lua
local gameConfig = require("gameConfig")
local Sublevel = require("Sublevel")
local EnemyPlane = require("enemies.EnemyPlane")
local myLevel = Sublevel.new("9999999-016", "level name", "author name", {duration = 4000})
function myLevel:show(options)
local enemy = EnemyPlane.new()
self:insert(enemy)
--place the enemy out of the screen
enemy.x = gameConfig.contentWidth/4
enemy.y = -100
print(self.gameMode)
if self.gameMode == gameConfig.MODE_SINGLE_LEVEL then
enemy:setScaleLinearVelocity( 0, 100 )
elseif self.gameMode == gameConfig.MODE_INFINITE_LEVEL then
enemy:setScaleLinearVelocity( 15, 100 )
end
--destroy the enemy properly
enemy:autoDestroyWhenInTheScreen()
end
return myLevel