昨天我們終於完成到可以炸掉敵人,但是我們還必須處理炸掉箱子、不小心炸到玩家的處理以及勝負判斷的邏輯,雖然看起來很多項,但其實要加上的程式碼不多,特別是在前面我們已經繪製了好幾個流程圖,要接續去撰寫其實並不困難,所以我想這個章節其實可以當作一個中場休息,同時也小回顧一下前面的內容,再接著用前面提過的方法來練習今天要處理的邏輯。
回到整個系列的命題本身,我期待用打造 CLI 好玩遊戲區這個專案來練習思考這件事,現在來到了第十五天,也剛好很適合檢視一下自己到底有沒有好好的練習到思考,以及驗證自己是否有透過這個專案來學到思考的一些方法,這邊幫我自己也幫大家回顧一下前面我們提到的一些寫程式上的實用方法:
另外我也在寫作的過程中整理自己在面對問題時會運用哪些方法來判斷下一步該怎麼走,包含技術的選擇、遊戲的設計,甚至是面對哪些要保留、哪些則可以捨去的產品邊界問題時,我會運用這些心法來幫助自己:
整理到這邊感覺好像終於可以放下心中的一個大石頭,確認自己是真的有認真在重建自己思考的方式,回顧系列開場前,其實一直感覺這個被 AI 養大的自己已經完全失去思考的能力了,但到了這裡好像終於有點自信能說「我其實是有思考的能力的」。
那麼我們就在接下來的章節中,繼續來使用這些思考的方式,持續地來練習把專案完成吧!
一樣要再回顧一下流程圖,弄清楚我們還有哪些要處理的部分:


目前就是剩下要炸掉箱子之後清空箱子、炸掉玩家之後要重置玩家、儀表板數字要更新。不過好消息是從流程圖中我們發現其實渲染都是用昨天我們寫好的清除敵人之後的那一個渲染流程其實就可以了。
箱子因為是陣列,因此我們只需要把它從陣列中清除就可以了,以下是我的步驟拆解:
因為是爆炸之後發生的邏輯 所以我們就把他加進爆炸之後:
for(let i = 0 ; i < surroundingCells.length; i++){
explodeCells = explodeCells.filter(cell => cell !== surroundingCells[i])
// 這裡加上判斷這一格爆炸區是否存在箱子,存在的話就篩選掉,把剩餘的存回原本的陣列
boxes = boxes.filter(box => !(box.x === surroundingCells[i].x && box.y === surroundingCells[i].y));
}
bombs = bombs.filter(item => !(item.x === bombX && item.y === bombY));
// 加在渲染之前
setTimeout(()=>render(),1000);
在這邊我們利用 filter 方法,篩選掉存在於爆炸區域內的箱子,只保留不存在於爆炸區域的箱子,接著後面是既有的清除炸彈和一秒後重新渲染的邏輯,保留不動就可以了。
試玩看看吧!
確認可以清除掉箱子就 OK 了!
一樣先拆解步驟
// 有玩家的處理
const hasPlayer = surroundingCells.find(cell => cell.x === player.x && cell.y === player.y);
if(hasPlayer) {
HP -= 1;
}
把這段邏輯也是寫到同一個函式內之後,我突然在思考「隨機重置位置」這件事情是否有必要呢?如果沒有這個重置的邏輯,遊戲是否可以正常遊玩呢?
利用前面我提過的判斷方法,我們的目的是要讓爆爆王這個遊戲可以順利地玩起來,那麼如果不加上這個前面繪製流程圖時臨時想到的功能,遊戲是否還能照常進行,如果可以的話,就表示這個功能屬於非必要,除非少了這個功能就無法遊玩,否則不一定需要加上這個功能。
先來玩看看就知道了:
從遊戲的結果看來,爆炸之後玩家停留在原地似乎沒有造成什麼影響,所以我決定先跳過這個實作。
儀表板目前只有兩個數字:敵人數量和HP,剛剛我們已經有做爆炸之後更新 HP 數值的邏輯,我們現在唯一需要做的只有把 HP 的初始數值改成我們預期的 3,如果你想要多一點血量也可以改高一點XD
let HP = 3;
接著要處理的就是在敵人陣亡之後去計算目前還有多少敵人存活,首先我們要處理一下 enemyNum:讓他對應敵人的生存狀態計算出數量。
const enemies = [enemy1,enemy2,enemy3];
let enemyNum = enemies.filter(enemy => enemy.live === true).length;
然後要在炸彈爆炸之後補上重新計算:
// 有敵人的處理
const hasEnemy1 = surroundingCells.find(cell => cell.x === enemy1.x && cell.y === enemy1.y);
const hasEnemy2 = surroundingCells.find(cell => cell.x === enemy2.x && cell.y === enemy2.y);
const hasEnemy3 = surroundingCells.find(cell => cell.x === enemy3.x && cell.y === enemy3.y);
if (hasEnemy1) {
enemy1.live = false;
}
if (hasEnemy2) {
enemy2.live =false;
}
if (hasEnemy3) {
enemy3.live =false;
}
// 加上這一句來計算存活的敵人有幾個,並把數值存回 enemyNum
enemyNum = enemies.filter(enemy => enemy.live === true).length;
一樣因為後面有一個已經寫好的 render ,所以我們寫上這一段之後就不用再寫一次渲染邏輯,直接執行出來看一下:
做完儀表板之後,看著數字降到 0 總覺得少了點什麼,所以臨時決定!我要一起把勝負判定的規則給寫起來。
其實獲勝和失敗的判定非常簡單:
if(enemyNum <= 0) {
process.stdout.write("\n所有敵人都被消滅,恭喜獲勝\n");
process.exit();
}
if(HP <= 0) {
process.stdout.write("\n失去所有HP,遊戲結束\n");
process.exit();
}
我們就先分別加在敵人和玩家爆炸後的處理後面試試看能不能玩得起來,完整程式碼再請大家參考下方列出來的今日進度唷,直接來看一下結果:
到這邊我們就終於從炸彈爆炸畢業了,那麼今天的進度就完成啦,很感謝大家陪我走到一半了!明天我們終於要進入到我最期待的 AI 敵人的實作,如果你有想法可以先試著寫出來,你對於 AI 敵人有哪些期待呢?
如果你想要瀏覽完整的程式碼,可以參考這裡:第15天程式碼