第三節 實現方塊消除和遊戲失敗邏輯
在上一節我們實現了形狀的旋轉和移動功能,那隻要再實現標題中的這兩個功能,俄羅斯方塊這個遊戲基本上也就算完成了(可以動手玩了)。
方塊消除
判斷是否要消除的方塊的邏輯很簡單,只需要檢查某一行是否被方塊填滿即可——也就是說,如果某一行上所有方塊的寬度總和等於屏幕寬度的話,那麼我們就可以消除這一行了。
代碼如下:
// Game.js
removeLines() {
// 消除
let lines = []; // 用於記錄被消除的行編號(第幾行)
for (let i=0; i<this.row; i++) {
let tempWidth = 0; // 用於判斷是否進行消除
let tempTile = []; // 存儲某一行上要被消除的方塊預製
let y = Math.round(-i*this.tileHeight); // 當前行的y值
// 判斷confirmedTileArray中方塊的y值跟當前行的y值是否相同
for (let j=0; j<this.confirmedTileArray.length; j++) {
let confirmedY = Math.round(this.confirmedTileArray[j].y);
if (y == confirmedY) {
tempTile.push(this.confirmedTileArray[j]); // 如果相同則存儲該方塊
tempWidth += this.tileWidth; // 並增加tempWidth值
}
}
// 判斷tempWidth值是否等於(或超過)shapeBoard的寬度,若超過,則說明該行已被填滿
if (tempWidth >= this.shapeBoard.width) {
lines.push(i);
tempTile.forEach(e=>{
// 從confirmedTileArray中刪除相關方塊
for (let j=0; j<this.confirmedTileArray.length; j++) {
if (e == this.confirmedTileArray[j])
this.confirmedTileArray.splice(j, 1);
}
this.tilePool.put(e); // 回收方塊
});
}
}
},
- 首先循環所有行,每次循環時查找confirmedTileArray中哪些方塊的y值跟當前行y值相同,這樣就可以知道這一行上有所少個方塊了。
- 每找到一個方塊,那我們就把它防暑temTile數組中,並且讓tempWidth值增加(一個方塊寬度)。
- 如果tempWidth等於(正常來講不會超過)屏幕寬度,那麼就說明這一行已經被填滿了,允許消除。
- 接着將當前行的編號放到lines數組中(用於之後的方塊下落),再從confirmedTileArray中刪除相關的方塊元素,當然也要把這個方塊回收到節點池中。
在moveDown方法中調用剛編寫好的removeLines(),注意只有方塊觸底和碰到其他方塊時才進行調用:
// Game.js
moveDown () {
// 往下移動一步
for (let i=0; i<this.shapeTileArray.length; i++) {
let x = Math.round(this.shapeTileArray[i].x);
let y = Math.round(this.shapeTileArray[i].y - this.tileHeight);
// 如果觸底,則不再下降
if (Math.abs(y) >= this.shapeBoard.height) {
this.shapeTileArray.forEach(element => { // 將確定的方塊放入this.confirmedTileArray
this.confirmedTileArray.push(element);
});
this.removeLines(); // 消除方塊
this.makeShape(); // 重新生成形狀
return false;
}
// 如果碰到其他方塊,則不再下降
for (let j=0; j<this.confirmedTileArray.length; j++) {
let confirmedX = Math.round(this.confirmedTileArray[j].x);
let confirmedY = Math.round(this.confirmedTileArray[j].y);
if (confirmedX==x && confirmedY==y) {
this.shapeTileArray.forEach(element => { // 將確定的方塊放入this.confirmedTileArray
this.confirmedTileArray.push(element);
});
this.removeLines(); // 消除方塊
this.makeShape(); // 沒輸則重新生成形狀
return false;
}
}
}
...
},
此時我們運行遊戲,發現滿足條件後方塊可以被正常消除:
不過消除之後其他方塊並未下落。所以我們還需要編寫一個dropConfirmedTiles方法:
// Game.js
dropConfirmedTiles (lines) {
// 讓其他未消除的方塊下落
for (let i=0; i<lines.length; i++) {
for (let j=0; j<this.confirmedTileArray.length; j++) {
let confirmedY = Math.round(this.confirmedTileArray[j].y);
// 只有消除行上方的方塊才允許下降
if (confirmedY <= -lines[i]*this.tileHeight)
continue;
this.confirmedTileArray[j].y -= this.tileHeight;
}
}
},
不過請注意,並不是所有的方塊都會下落,只有在消除行上方的方塊纔可以。
記得在removeLines方法最後調用dropConfirmeTile():
// Game.js
removeLines() {
...
// 讓其他未消除的方塊下落
if (lines.length)
this.dropConfirmedTiles(lines);
},
遊戲失敗
當某一形狀中的各方塊下落完畢後,如果任一方塊超出屏幕頂端,則遊戲失敗:
// Game.js
judgeLose() {
for (let i=0; i<this.confirmedTileArray.length; i++) {
let confirmedY = Math.round(this.confirmedTileArray[i].y);
// 如果有任何一個方塊超出頂端,則輸
if (confirmedY >= 0)
return true;
}
return false;
},
循環confirmedTileArray,讓我們看看是哪個方塊超出了屏幕頂端(通過y值判斷)。
遊戲失敗了就要有失敗的樣子。在層級管理其中添加一個Sprite類型節點顯示Game Over圖片,以及一個重新開始按鈕。
將gameOver的透明度設爲0,然後隱藏restartBtn節點(遊戲失敗了我們再顯示):
編寫lose方法,該方法在遊戲判定失敗後調用:
// Game.js
lose () {
// 遊戲失敗
this.unschedule(this.moveDown); // 取消計時器
let fadeInAction = cc.fadeIn(1);
this.gameOverNode.runAction(fadeInAction); // 顯示Game Over圖片
this.restartNode.active = true; // 顯示restart按鈕
},
Game Over圖片我們用漸顯,而restart按鈕則直接通過active進行顯示。
記得在properties中添加以下兩個屬性:
// Game.js
properties: {
...
gameOverNode: cc.Node,
restartNode: cc.Node,
},
restart按鈕的事件函數非常簡單,重新載入當前場景即可:
// Game.js
restart() {
// 重新開始
cc.director.loadScene('俄羅斯方塊');
},
最後我們在moveDown方法中調用judeLose()判斷遊戲是否失敗就行啦:
moveDown () {
// 往下移動一步
for (let i=0; i<this.shapeTileArray.length; i++) {
...
// 如果碰到其他方塊,則不再下降
for (let j=0; j<this.confirmedTileArray.length; j++) {
let confirmedX = Math.round(this.confirmedTileArray[j].x);
let confirmedY = Math.round(this.confirmedTileArray[j].y);
if (confirmedX==x && confirmedY==y) {
this.shapeTileArray.forEach(element => { // 將確定的方塊放入this.confirmedTileArray
this.confirmedTileArray.push(element);
});
if (this.judgeLose()) {
this.lose(); // 遊戲失敗
}
else {
this.removeLines(); // 消除方塊
this.makeShape(); // 沒輸則重新生成形狀
}
return false;
}
}
}
...
},
運行截圖如下:
雖然標準的俄羅斯方塊遊戲沒有"贏"這一說法,但規矩是死的,我們完全可以發揮自己的想象力讓俄羅斯方塊可以贏對吧,比如可以給遊戲添加關卡功能。大家可以點擊該鏈接去了解下如何實現這一功能。