關卡功能實現
在上一節教程中我們講解了打磚塊的主要功能與邏輯實現,在這一篇教程中,筆者會在它的基礎上增加關卡功能(建議先閱讀上一節教程)。
運行效果如下:
Cocos Creator版本:2.2.0
後臺回覆"關卡",獲取該項目完整文件:
創建節點
1. bg就是一個背景圖片。
2. levelsLayout爲一個佈局節點,節點上的Layout組件屬性設置如下(除了Type和Resize Mode,其他屬性大家可以根據需要自行修改):
該節點用來對各個關卡進行佈局,完成後樣子如下:
3. level是一個預製節點,其子節點num用於提示當前關卡數,star節點用於提示是否已經通關(沒有通關的時候爲灰色,通關後變爲黃色)。另外num節點初始狀態不可見,只有玩家通過當前關卡後,後面一個關卡的數字纔會顯示(當然第一關在一開始就要顯示數字)。
設置關卡信息
我們在scripts文件夾中新建一個Settings.js腳本,編寫如下代碼:
// Settings.js
let settings = [
{
level: 1, // 第1關
row: 3, // 行數
col: 3, // 列數
spaceX: 20, // 列間隔
spaceY: 20, // 行間隔
brickWidth: 200, // 磚塊寬度
brickHeight: 100, // 磚塊高度
levelState: 'UNLOCKED', // 關卡狀態
transparentBricks: [[1,0], [2,2]] // 剛開始就透明的磚塊
},
{
level: 2, // 第2關
row: 6,
col: 6,
spaceX: 10,
spaceY: 10,
brickWidth: 120,
brickHeight: 70,
levelState: 'LOCKED',
transparentBricks: [[3,5], [4,1], [3,4]]
},
{
level: 3, // 第3關
row: 9,
col: 9,
spaceX: 10,
spaceY: 10,
brickWidth: 100,
brickHeight: 50,
levelState: 'LOCKED',
transparentBricks: [[7,5], [3,1], [5,7],[7,2],[6,8],[7,7]]
},
{
level: 4, // 第4關
row: 12,
col: 15,
spaceX: 5,
spaceY: 5,
brickWidth: 80,
brickHeight: 40,
levelState: 'LOCKED',
transparentBricks: [[1,1], [2,2], [3,3],[4,4],[5,5],[6,6],[7,7],[8,8],[9,9]]
},
{
level: 5, // 第5關
row: 12,
col: 15,
spaceX: 5,
spaceY: 5,
brickWidth: 80,
brickHeight: 40,
levelState: 'LOCKED',
transparentBricks: [[1,1], [2,2], [3,3],[4,4],[5,5],[6,6],[7,7],[8,8],[9,9]]
},
{
level: 6, // 第6關
row: 13,
col: 13,
spaceX: 4,
spaceY: 4,
brickWidth: 60,
brickHeight: 30,
levelState: 'LOCKED',
transparentBricks: [[9,9], [1,1], [3,4],[5,4],[5,6],[7,6],[7,8],[8,10],[9,1]]
},
]
export {settings};
在這個腳本中我們就新建了一個JSON變量settings,其中每個數組元素都包含了相應關卡的具體配置(這裏共有六個關卡)。這裏筆者講一下levelState和transparentBricks這兩個鍵:
- levelState用來判斷當前關卡的狀態:未解鎖,解鎖或通過。
- 通過控制transparentBricks我們就可以創建出形狀各異的磚塊佈局。
最後用export輸出該變量,我們會在其他腳本中調用。
實現關卡界面
新建Select.js腳本,編寫如下代碼:
// Select.js
import {settings} from './Settings.js';
cc.Class({
extends: cc.Component,
properties: {
levelPrefab: cc.Prefab,
levelsLayout: cc.Node
},
// LIFE-CYCLE CALLBACKS:
onLoad () {
this.initLevels();
},
initLevels () {
if (!cc.sys.localStorage.getItem('settings')) {
for (let i=0; i<settings.length; i++) {
let level = cc.instantiate(this.levelPrefab);
level.settings = settings[i];
level.getComponent('Level').changePic(settings[i]['levelState'], (i+1).toString());
this.levelsLayout.addChild(level);
}
// 將所有關卡信息存入本地(針對首次遊戲)
cc.sys.localStorage.setItem('settings', JSON.stringify(settings));
}
else {
// 如果玩家已經玩過,則從本地存儲中獲取關卡配置信息
let newSettings = JSON.parse(cc.sys.localStorage.getItem('settings'));
for (let i=0; i<newSettings.length; i++) {
let level = cc.instantiate(this.levelPrefab);
level.settings = newSettings[i];
level.getComponent('Level').changePic(newSettings[i]['levelState'], (i+1).toString());
this.levelsLayout.addChild(level);
}
}
}
});
下面是對該腳本代碼的解釋:
- 在該腳本的開頭我們導入了Settings.js中的setting變量。
- 在properties中添加levelPrefab和levelsLayout屬性。
- initLevels方法用於創建各個關卡預製,並添加到佈局中。
在initLevels方法中,我們首先判斷本地存儲中是否有settings項。
如果沒有的話,那麼說明玩家第一次玩,於是我們就新建預製,並將Settings.js中的各個關卡信息保存到相應預製中,調用changePic方法來設置預製相應的圖片(該方法會根據levelState來設置相應的圖片)。最後我們要將所有關卡信息存入本地,我們之後也會在Game.js的win方法中更新關卡信息,而遊戲以後也都會只從本地存儲中讀取關卡信息了(當然可以選擇把本地存儲改爲服務器存儲)。
如果有的話,那麼說明玩家不是第一次玩了,於是我們就從本地中讀取關卡信息,創建並設置預製。
爲預製添加腳本
新建Level.js腳本,添加代碼如下:
// Level.js
cc.Class({
extends: cc.Component,
properties: {
unlockedPic: cc.SpriteFrame,
lockedPic: cc.SpriteFrame,
greyStarPic: cc.SpriteFrame,
yellowStarPic: cc.SpriteFrame,
},
// LIFE-CYCLE CALLBACKS:
onLoad () {
// 觸摸監聽
this.node.on('touchstart', this.onTouchStart, this);
},
changePic (levelState, num) {
// 更改圖片
if (levelState == 'UNLOCKED') {
// 解鎖關卡
this.node.children[0].active = true;
this.node.children[0].getComponent(cc.Label).string = num;
this.node.getComponent(cc.Sprite).spriteFrame = this.unlockedPic;
this.node.children[1].getComponent(cc.Sprite).spriteFrame = this.greyStarPic;
}
else if (levelState == 'PASSED') {
// 通關
this.node.children[0].active = true;
this.node.children[0].getComponent(cc.Label).string = num;
this.node.getComponent(cc.Sprite).spriteFrame = this.unlockedPic;
this.node.children[1].getComponent(cc.Sprite).spriteFrame = this.yellowStarPic;
}
else if (levelState == 'LOCKED') {
// 關卡未解鎖
this.node.getComponent(cc.Sprite).spriteFrame = this.lockedPic;
this.node.children[1].getComponent(cc.Sprite).spriteFrame = this.greyStarPic;
}
},
onTouchStart () {
if (this.node.settings['levelState'] == 'LOCKED')
return;
// 將目標關卡信息存入本地,在Game.js中取出
cc.sys.localStorage.setItem('currentLevelInfo', JSON.stringify(this.node.settings));
cc.director.loadScene('打磚塊');
}
});
下面是對該腳本代碼的解釋:
- 在properties中添加四個屬性,都是SpriteFrame類型。前兩張圖片用於關卡背景,後兩張用於星星。
- 在onLoad方法中添加觸摸監聽。在onTouchStart方法中我們首先根據關卡的levelState來判斷它是否已經解鎖,如果已經解鎖則將當前關卡信息存入currentLevelInfo項中,然後進入打磚塊場景。
- 在changgePic方法中,我們同樣根據levelState來設置相應圖片。相信看代碼大家可以看懂,這裏筆者就不再贅述。
完善Game.js邏輯
在Game.js腳本中,我們首先在onLoad方法中獲取currentLevelInfo項,並設置相關變量:
// Game.js
onLoad () {
...
// 首先獲取當前關卡信息
let currentLevelInfo = JSON.parse(cc.sys.localStorage.getItem('currentLevelInfo'));
this.level = currentLevelInfo['level']; // 第幾關
this.row = currentLevelInfo['row']; // 行數
this.col = currentLevelInfo['col']; // 列數
this.spaceX = currentLevelInfo['spaceX']; // 列間隔
this.spaceY = currentLevelInfo['spaceY']; // 行間隔
this.brickWidth = currentLevelInfo['brickWidth']; // 磚塊寬度
this.brickHeight = currentLevelInfo['brickHeight']; // 磚塊高度
this.transparentBricks = currentLevelInfo['transparentBricks']; // 剛開始就透明的磚塊
this.speed = 20; // bar移動速度
...
},
然後在initBricksLayout方法中根據當前關卡中的transparentBrick來設置透明磚塊:
// Game.js
initBricksLayout () {
...
// 循環放置磚塊
for (let i=0; i<this.row; i++) {
for (let j=0; j<this.col; j++) {
...
// 看看是不是透明磚塊
for (let k=0; k<this.transparentBricks.length; k++) {
if (this.transparentBricks[k][0]==i && this.transparentBricks[k][1]==j) {
brick.opacity = 0; // 變透明
brick.removeComponent(cc.RigidBody); // 移出剛體組件,讓該磚塊無法與球碰撞
}
}
}
}
},
最後我們在win方法中更新關卡信息,只用更改關卡中的levelState就行啦:
// Game.js
win () {
// 更新關卡信息
let settings = JSON.parse(cc.sys.localStorage.getItem('settings'));
settings[this.level-1]['levelState'] = 'PASSED'; // 當前關卡狀態變爲通過(數組下標-1)
settings[this.level]['levelState'] = 'UNLOCKED'; // 下一關卡狀態變爲解鎖
cc.sys.localStorage.setItem('settings', JSON.stringify(settings));
console.log('恭喜過關!');
cc.director.loadScene('關卡');
},
好了,那麼今天的教程就到這,希望大家有所收穫~