iT邦幫忙

2026 iThome 鐵人賽

DAY 15
0
JavaScript

重新拿回思考力!一起來用 JavaScript 打造 CLI 好玩遊戲區!系列 第 15 篇

Day15 爆爆王7:來個中場休息小回顧

  • 分享至 

  • xImage
  •  

昨天我們終於完成到可以炸掉敵人,但是我們還必須處理炸掉箱子、不小心炸到玩家的處理以及勝負判斷的邏輯,雖然看起來很多項,但其實要加上的程式碼不多,特別是在前面我們已經繪製了好幾個流程圖,要接續去撰寫其實並不困難,所以我想這個章節其實可以當作一個中場休息,同時也小回顧一下前面的內容,再接著用前面提過的方法來練習今天要處理的邏輯。

回到整個系列的命題本身,我期待用打造 CLI 好玩遊戲區這個專案來練習思考這件事,現在來到了第十五天,也剛好很適合檢視一下自己到底有沒有好好的練習到思考,以及驗證自己是否有透過這個專案來學到思考的一些方法,這邊幫我自己也幫大家回顧一下前面我們提到的一些寫程式上的實用方法:

  1. 用問問題的方式拆解步驟
  2. 用自己的語言描述在轉化成程式碼內的邏輯
  3. 先窮舉再精簡
  4. 從想要得到怎樣的結果來反推函式
  5. 把複雜的步驟轉為功能流程圖
  6. 利用繪製時序圖來幫助自己了解程式執行的順序

另外我也在寫作的過程中整理自己在面對問題時會運用哪些方法來判斷下一步該怎麼走,包含技術的選擇、遊戲的設計,甚至是面對哪些要保留、哪些則可以捨去的產品邊界問題時,我會運用這些心法來幫助自己:

  1. 不在一開始就追求最佳解答,而是在實作中去優化,才更能知道為什麼要這樣走
  2. 依據使用者反饋,提出可能性再決定執行的方向
  3. 藉由打造原型來實測可行性
  4. 卡住的時候對外尋求幫助
  5. 想法太多的時候先釐清當下的目標是什麼,藉此來做判斷

整理到這邊感覺好像終於可以放下心中的一個大石頭,確認自己是真的有認真在重建自己思考的方式,回顧系列開場前,其實一直感覺這個被 AI 養大的自己已經完全失去思考的能力了,但到了這裡好像終於有點自信能說「我其實是有思考的能力的」。

那麼我們就在接下來的章節中,繼續來使用這些思考的方式,持續地來練習把專案完成吧!

流程圖回顧

一樣要再回顧一下流程圖,弄清楚我們還有哪些要處理的部分:

https://ithelp.ithome.com.tw/upload/images/20260927/20182439IACI6grw6Q.png

https://ithelp.ithome.com.tw/upload/images/20260927/20182439E7Wdo2YG3j.png

目前就是剩下要炸掉箱子之後清空箱子、炸掉玩家之後要重置玩家、儀表板數字要更新。不過好消息是從流程圖中我們發現其實渲染都是用昨天我們寫好的清除敵人之後的那一個渲染流程其實就可以了。

有箱子的處理

箱子因為是陣列,因此我們只需要把它從陣列中清除就可以了,以下是我的步驟拆解:

  1. 偵測爆炸區內是否存在箱子
  2. 從陣列中清除掉這個箱子
  3. 重新渲染(昨天已經寫好)

因為是爆炸之後發生的邏輯 所以我們就把他加進爆炸之後:

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 了!

有玩家的處理

一樣先拆解步驟

  1. 判斷爆炸區內是否有玩家
  2. 玩家要扣一滴血,判斷是不是還活著
  3. 隨機重置位置
  4. 重新渲染:一樣跟前面的步驟用同一次渲染
 // 有玩家的處理
    const hasPlayer = surroundingCells.find(cell => cell.x === player.x && cell.y === player.y);
    if(hasPlayer) {
        HP -= 1;
    }

把這段邏輯也是寫到同一個函式內之後,我突然在思考「隨機重置位置」這件事情是否有必要呢?如果沒有這個重置的邏輯,遊戲是否可以正常遊玩呢?

利用前面我提過的判斷方法,我們的目的是要讓爆爆王這個遊戲可以順利地玩起來,那麼如果不加上這個前面繪製流程圖時臨時想到的功能,遊戲是否還能照常進行,如果可以的話,就表示這個功能屬於非必要,除非少了這個功能就無法遊玩,否則不一定需要加上這個功能。

先來玩看看就知道了:

HP更新

從遊戲的結果看來,爆炸之後玩家停留在原地似乎沒有造成什麼影響,所以我決定先跳過這個實作。

儀表板更新

儀表板目前只有兩個數字:敵人數量和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();
}
  • 失敗:HP 歸零。
if(HP <= 0) {
  process.stdout.write("\n失去所有HP,遊戲結束\n");
  process.exit();
}

我們就先分別加在敵人和玩家爆炸後的處理後面試試看能不能玩得起來,完整程式碼再請大家參考下方列出來的今日進度唷,直接來看一下結果:

勝負判定

到這邊我們就終於從炸彈爆炸畢業了,那麼今天的進度就完成啦,很感謝大家陪我走到一半了!明天我們終於要進入到我最期待的 AI 敵人的實作,如果你有想法可以先試著寫出來,你對於 AI 敵人有哪些期待呢?

如果你想要瀏覽完整的程式碼,可以參考這裡:第15天程式碼


上一篇
Day14 爆爆王6:把隱藏在背景程序中的執行順序整理成時序圖
下一篇
Day16 爆爆王8:移動一格背後的秘密,從設計師角度看AI 敵人設計
系列文
重新拿回思考力!一起來用 JavaScript 打造 CLI 好玩遊戲區! 共 19 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言