打磚塊主要功能與邏輯實現
在本節教程中,我們來完成打磚塊遊戲中的主要功能與邏輯。
運行效果如下:
Cocos Creator版本:2.2.0
後臺回覆"打磚塊",獲取該項目完整文件:
資源文件和節點
首先我們來看下該項目中的資源文件有哪些:
1. audios文件夾
- ball音頻在小球碰到牆壁或者條形板時播放
- hit音頻在小球碰到磚塊時播放
2. prefabs文件夾
- 因爲要生成多個方塊,可想而知要把brick節點變爲預製
3. scripts文件夾
- Ball.js會掛載到ball節點上,用於處理小球邏輯
- Brick.js會掛載到brick節點上(也就是說預製上有Brick組件),用於處理磚塊邏輯
- Game.js會掛載到Canvas節點上,處理整個遊戲邏輯
4. textures文件夾
- ball爲小球圖片
- bar爲條形板
- bg爲遊戲背景
- brick爲磚塊圖片
再來看下節點:
1. 第一個Canvas節點(設計分辨率爲1080x1920),我們需要爲它掛上一個Chain物理組件來設置一個三面的牆(上左右),目的是讓小球在牆裏運動:
剛體組件(RigidBody)設置如下:
PhysicsChainCollider組件設置如下:
此時碰撞邊框剛好與Canvas邊框重疊(除了底部):
2. bg爲背景圖片節點。
3. bricksLayout節點用於放置磚塊,它上面掛有Layout組件,設置如下:
- type設置爲GRID(網格佈局)
- Resize Mode設置爲CHILDREN模式
- 其餘不用管,我們之後會在代碼中調整
筆者還爲bricksLayout加上了Wight組件:
3. bar節點爲條形板,我們同樣爲它加上物理碰撞組件(具體屬性設置請看項目,筆者這裏就不再贅述):
4. ball節點爲小球,也要加上物理碰撞組件:
5. brick節點爲磚塊,我們給它掛上物理碰撞組件和Brick.js腳本組件後,將其轉爲預製:
添加分組
分組管理設置如下:
小球可以跟磚塊、牆和板碰撞,其餘組件相互之前不碰撞。
請大家自行給各個節點設置分組,筆者這裏就不截圖了。
編寫腳本
我們首先來看下Brick.js腳本,內容如下:
// Brick.js
cc.Class({
extends: cc.Component,
properties: {
},
// LIFE-CYCLE CALLBACKS:
onLoad () {
// 設置磚塊顏色
this.node.color = this.randomColor();
},
randomColor () {
// 獲取隨機顏色
let red = Math.round(Math.random()*255);
let green = Math.round(Math.random()*255);
let blue = Math.round(Math.random()*255);
return new cc.Color(red, green, blue);
},
// update (dt) {},
});
很簡單,就是給每個生成的磚塊添上一個隨機顏色。完成的效果如下圖所示:
再來看下Ball.js腳本:
// Ball.js
cc.Class({
extends: cc.Component,
properties: {
ballAudio: {
type: cc.AudioClip,
default: null
},
hitAudio: {
type: cc.AudioClip,
default: null
}
},
// LIFE-CYCLE CALLBACKS:
onLoad () {
// 球方向隨機
this.node.getComponent(cc.RigidBody).linearVelocity.x = Math.random() * -1200 + 600;
this.node.getComponent(cc.RigidBody).linearVelocity.y = 1000;
},
onBeginContact (contact, self, other) {
if (other.tag == 1) {
cc.audioEngine.playEffect(this.ballAudio);
}
else if (other.tag == 2) {
other.node.opacity = 0; // 變透明
other.node.removeComponent(cc.RigidBody); // 移出剛體組件,讓該磚塊無法與球碰撞
cc.audioEngine.playEffect(this.hitAudio);
}
}
// update (dt) {},
});
- properties中就兩個音頻
- 在onLoad方法中,筆者設置了小球的開始運動時的速度和方向
- 在onBeginContact中進行了碰撞監聽。如果目標碰撞物的tag爲1(牆或者條形板),那麼我們就播放ball音效即可。而如果是2(磚塊),那麼我們就設置磚塊的透明度爲0,並且移出剛體組件(因爲遊戲中磚塊不能被二次碰撞)
最後是Game.js腳本。我們首先在properties中添加如下屬性:
// Game.js
properties: {
brickPrefab: cc.Prefab, // 磚塊預製
bricksLayout: cc.Node, // 磚塊佈局
bar: cc.Node, // 條形板
ball: cc.Node // 小球
},
在onLoad方法中添加如下代碼:
// Game.js
onLoad () {
cc.director.getPhysicsManager().enabled = true; // 開啓物理引擎
// 爲了在下次教程中方便講解"關卡設置",筆者這裏用了以下變量來控制磚塊佈局
this.row = 9; // 行數
this.col = 9; // 列數
this.spaceX = 10; // 列間隔
this.spaceY = 10; // 行間隔
this.brickWidth = 100; // 磚塊寬度
this.brickHeight = 50; // 磚塊高度
this.speed = 20; // bar移動速度
this.bricksArray = []; // 磚塊數組
this.initBricksLayout(); // 初始化磚塊佈局
// 初始化鍵盤輸入監聽
this.moveDir = null; // 用於確定左右移動方向
cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);
cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this);
},
- 開啓物理引擎
- 添加相關變量用來控制磚塊佈局樣子(筆者在下一個教程"關卡設置"一文中拿打磚塊這個例子來講解如何給遊戲加上關卡功能)
- 生成的磚塊預製都會被加入到this.bricksArray數組變量中,更方便控制
- 最後我們調用initBricksLayout方法初始化磚塊佈局並且對鍵盤進行監聽(用於控制條形板左右移動)
initBricksLayout方法編寫如下:
// Game.js
initBricksLayout () {
// 設置bricksLayout相關屬性
this.bricksLayout.getComponent(cc.Layout).spacingX = this.spaceX;
this.bricksLayout.getComponent(cc.Layout).spacingY = this.spaceY;
this.bricksLayout.getComponent(cc.Layout).cellSize.width = this.brickWidth;
this.bricksLayout.getComponent(cc.Layout).cellSize.height = this.brickHeight;
this.bricksLayout.width = this.col*this.brickWidth + (this.col-1)*this.spaceX;
this.bricksLayout.height = this.row*this.brickHeight + (this.row-1)*this.spaceY;
// 循環放置磚塊
for (let i=0; i<this.row; i++) {
for (let j=0; j<this.col; j++) {
let brick = cc.instantiate(this.brickPrefab);
brick.x = 99999; // 爲了跟bricksLayout節點區分,好同步到物理引擎
// 設置磚塊的碰撞規定尺寸
brick.getComponent(cc.PhysicsBoxCollider).width = this.brickWidth;
brick.getComponent(cc.PhysicsBoxCollider).height = this.brickHeight;
// 將磚塊添加到數組和佈局中
this.bricksArray.push(brick);
this.bricksLayout.addChild(brick);
}
}
},
在該方法中筆者首先對bricksLayout節點上的Layout組件相關屬性進行了設置,有行間隔、列間隔、子節點寬高以及bricksLayout寬高這些屬性。然後通過兩層循環來放置磚塊,在這裏有兩點需要注意下:
- brick.x=99999;這行代碼是必要的,否則磚塊實例化後的初始位置就跟brickLayout節點的中間位置(0, 0)重了,導致處在最當中的磚塊節點沒有同步到物理引擎。大家可以把這行代碼去掉看下。
- 碰撞框的大小應該跟磚塊節點大小一樣。
接下來是控制條形板左右移動的代碼:
// Game.js
onKeyDown (event) {
// 控制bar左右移動
switch(event.keyCode) {
case cc.macro.KEY.a:
case cc.macro.KEY.left:
this.moveDir = 'left';
break;
case cc.macro.KEY.d:
case cc.macro.KEY.right:
this.moveDir = 'right';
break;
}
},
onKeyUp (event) {
this.moveDir = '';
},
- 當按下A鍵或者左鍵時,設置this.moveDir值爲'left'
- 當按下D鍵或者右鍵時,設置this.moveDir值爲'right'
- 當釋放鼠標後,設置this.moveDir值爲''
然後是update方法:
// Game.js
update (dt) {
// 移動bar
if (this.moveDir == 'left') {
this.bar.x -= this.speed;
}
else if (this.moveDir == 'right') {
this.bar.x += this.speed;
}
// 限制bar
let canvasWidth = cc.Canvas.instance.node.width;
let canvasHeight = cc.Canvas.instance.node.height;
if (this.bar.x < -canvasWidth/2+this.bar.width/2) {
this.bar.x = -canvasWidth/2+this.bar.width/2;
}
if (this.bar.x > canvasWidth/2-this.bar.width/2) {
this.bar.x = canvasWidth/2-this.bar.width/2;
}
// 根據小球位置來判斷輸贏
if (this.ball.y < -canvasHeight/2) {
this.lose();
}
},
- 根據this.moveDir值來改變條形板的x座標
- 將條形板限制在屏幕中
- 根據小球y值來判斷它是否掉到了底部,如果是的話則遊戲結束
lose方法如下:
// Game.js
lose () {
console.log('輸了= =');
cc.director.loadScene('打磚塊');
},
遊戲失敗邏輯筆者這裏就簡單編寫下,大家可以自行豐富內容。
最後我們再來看下游戲勝利邏輯。當磚塊全部被消滅後,遊戲自然就勝利了。那其實我們只用在小球每次跟磚塊碰撞時判斷下磚塊是否都消失了即可。首先在onLoad方法中添加如下代碼將Game實例傳入Ball.js中:
// Game.js
onLoad () {
...
this.ball.getComponent('Ball').init(this); // 將this傳到Ball.js中
...
},
遊戲勝利win方法如下:
// Game.js
win () {
console.log('恭喜過關!');
cc.director.loadScene('打磚塊');
},
之後在Ball.js中添加如下代碼:
// Game.js
cc.Class({
...
init(game) {
this.game = game;
},
onBeginContact (contact, self, other) {
if (other.tag == 1) {
cc.audioEngine.playEffect(this.ballAudio);
}
else if (other.tag == 2) {
other.node.opacity = 0; // 變透明
other.node.removeComponent(cc.RigidBody); // 移出剛體組件,讓該磚塊無法與球碰撞
cc.audioEngine.playEffect(this.hitAudio);
// 根據透明度循環判斷磚塊是否都被消滅
let isWin = true;
for (let i=0; i<this.game.bricksArray.length; i++) {
if (this.game.bricksArray[i].opacity!=0) {
isWin = false;
}
}
if (isWin) {
this.game.win();
}
}
}
// update (dt) {},
});
- init方法用於獲取Game實例
- 當小球碰到磚塊時,循環判斷bricksArray中各個小球的透明度,如果透明度都爲0,則遊戲勝利
好的那本節教程就到這,希望大家有所收穫~