iT邦幫忙

2026 iThome 鐵人賽

DAY 13
0
JavaScript

QA 的 JavaScript 初心之路:30 天做出一個能玩的戰鬥小遊戲《勇者 vs 小怪》系列 第 22 篇

Day22|第一階段我需要一張表,第二階段我需要一張流程圖

  • 分享至 

  • xImage
  •  

第二階段回顧:從「學語法」到「做決定」

Day 12 我做過一次回顧,那時候剛學完基礎語法。今天是第二階段(Day 13–21)的回顧,也是動手做正式作品前的最後一站。

這九天學了什麼

Day 主題 關鍵收穫
13 innerHTML vs textContent XSS、轉義、同一份資料有多個出口
14 style 與 classList 駝峰命名、四階段配色、陰影計數器
15 表單與輸入 輸入驗證、找到自己的漏洞、註解會過期
16 亂數 max - min + 1、用統計測不確定的系統
17 計時器 事件迴圈、setTimeout(fn,0) 不插隊、決定不用
18 狀態管理 單一事實來源、活參照陷阱放大版
19 非同步 Promise、async/await、卡住 vs 不卡住
20 localStorage 只能存字串、null 處理、資料安全的邊界
21 除錯 大聲 vs 安靜失敗、中斷點、四個 bug 實戰

最有成就感的一刻:網頁真的「有邏輯地在動」

如果要挑一個瞬間,是畫面第一次真的動起來的時候。

不是單純「有東西出現」,而是它有邏輯地在變化:按下攻擊,數字會扣、文字會換、顏色會跟著血量改變。而且——

這一切都發生在同一個頁面上,不需要切換頁面。

這件事我以前沒意識到有多特別。以前我對網頁的印象是:點一個連結,整個頁面重新載入,換一張新的畫面出來。但這個遊戲從頭到尾就是一頁,內容卻一直在變。

回頭看,這正是 JavaScript 在網頁裡的角色:HTML 決定有什麼,CSS 決定長什麼樣,而 JS 讓它們在使用者操作時改變,不用重新載入。

最難的一天:Day 21,藏在 bug 後面的 bug

這天我拿到一個被動過手腳的版本,要找出裡面的 4 個 bug。前三個還好,第三個(紀錄比較方向寫反)卡了很久,原因有兩層:

第一層:另一個 bug 擋住了它。 我需要連打三場才能比較紀錄,但「再戰一次」的 bug 讓我根本進不了第二場。

第二層,而且更尷尬:我被自己設計的戰鬥力搞到沒辦法觸發 bug。

用 F5 繞過之後,我連打三場,結果是 4、4、4。以現在的攻擊力和 ±3 的傷害浮動,每一場都剛好 4 回合結束 —— 根本沒有變化可以觀察。

最後是靠直接修改 localStorage 的紀錄值,人工造出比較的條件才抓到。

這件事有兩個收穫:一是「測試環境要能製造出各種情況」比我想的重要;二是這個遊戲的傷害浮動太小了,如果每個人都是 4 回合,那「最少回合挑戰」就沒有意義。這個問題要在組裝階段處理。

兩個階段的差別:我需要的東西變了

這是這次回顧最明確的一個感覺。

第一階段(Day 1–12)學語法的時候,我的感覺是:

嗯……沒有畫面、沒有回饋感,感覺之後一定會忘記。我需要一張表。

所以 Day 12 我真的做了一張速查表,依「我想做什麼」分類,卡住的時候直接查。那時候的困難是記不住。

第二階段(Day 13–21)的感覺完全不同:

喔~這個可以放進遊戲裡試試。我需要設計一張遊戲流程圖。

同樣是「我需要一個東西」,但需要的東西變了 —— 從速查表變成流程圖。

這個轉變說明了工作的性質改變了:

第一階段 第二階段
主要的困難 記不住語法 想不清楚流程
需要的輔助 速查表(查「怎麼寫」) 流程圖(想「怎麼走」)
每天的問題 這個怎麼寫? 這個該不該做?做了會怎樣?

第二階段有好幾天我學完之後的結論是「知道它能做什麼,然後決定不用」——Day 17 的計時器(會破壞回合制)、Day 19 的非同步(這個專案用不到)。第一階段不會有這種事,那時候學什麼就用什麼。

這大概就是從「學語法」變成「做決定」的意思。

所以我畫了流程圖

既然需要,就動手畫。

[開啟頁面] → [讀取紀錄] → [等待輸入名字]
                              ↓ 按「出征」
                         [驗證] ──失敗→ 顯示錯誤,名字不變
                              ↓ 通過
                         [準備就緒]
                              ↓ 按「攻擊」
      ┌───────────────── 戰鬥回合 ─────────────────┐
      │ [第一次?] → 鎖定名字輸入                     │
      │ [已結束?] ─是→ [鞭屍計數 +1] → 結束          │
      │      ↓ 否                                   │
      │ [算傷害 = max(1, atk-def ±3)] → [扣血]      │
      │      ↓                                      │
      │ [敵人死了?] ─否→ [回合 +1] ─┐               │
      │      ↓ 是                   │               │
      │ [歸零/標記結束/存紀錄]       │               │
      │      ↓                      │               │
      │ [更新畫面] ←────────────────┘               │
      └─────────────────────────────────────────────┘
                              ↓ 按「再戰一次」
                         [重置狀態 + 畫面] → 回到「準備就緒」

畫出來之後立刻發現一件事:「重置」要還原的東西比我以為的多。

欄位 重置時要還原嗎
hero.hp / enemy.hp 是
turn 是
whipCount 是
battleStarted 是(Day 20 抓到的不一致)
isOver 是(Day 21 的那個 bug)
localStorage 紀錄 否(跨場保留)

Day 21 那個 bug 就是漏了 isOver。如果早一點畫這張圖,那個 bug 根本不會存在 —— 因為圖上一眼就看得出哪些東西該還原。

最終作品:確定要做什麼、不做什麼

已經有的零件
角色資料、傷害計算(含亂數與保底)、自訂名字(含驗證與轉義)、回合計數、勝負判定、鞭屍彩蛋、四階段配色、狀態集中管理、最少回合紀錄、再戰一次。

還要補的(Day 23–29)

  • 小怪反擊 ← 最重要,目前牠只會站著挨打
  • 勇者陣亡判定與失敗畫面(目前只有勝利)
  • 受擊震動(Day 17 決定的表現層效果)
  • 血條(讓血量看得見,不只是數字)
  • CSS 排版、部署到 GitHub Pages

確定不做
中毒每秒扣血、限時挑戰(這兩個 Day 17 判斷過會破壞回合制)、多關卡、道具技能、雲端英雄榜 —— 最後三個留給之後的「勇闖魔王城」。

加入反擊之後,流程會多一個判斷:

[玩家攻擊] → [敵人死了?] ─是→ [勝利]
                  ↓ 否
             [敵人反擊] → [勇者死了?] ─是→ [失敗]
                  ↓ 否
             [回合 +1]

注意「敵人死了嗎」要先判斷,不然會變成「小怪死了還打你一下」。這種順序問題不寫出來很容易做錯 —— 又是流程圖的價值。

還想給自己藏一個彩蛋

最後一件事,我想在作品裡藏一個小彩蛋:輸入特殊名稱,就能從魔王的視角戰鬥。

整個系列都在打小怪,如果有人發現自己也能當魔王,應該會很有趣。

不過這個先不細講 —— 藏起來的東西,講出來就不叫彩蛋了。

小結

  • 最有成就感:畫面有邏輯地在動,而且全部發生在同一頁
  • 最難:Day 21,一個 bug 擋住另一個 bug,而且我被自己的數值設計卡住
  • 第一階段需要速查表(記不住),第二階段需要流程圖(想不清楚)
  • 第二階段學會的不只是語法,還有「決定不做什麼」
  • 流程圖畫出來的當下,就看見了 Day 21 那個 bug 的根源

明天開始正式組裝。第一件事:把擠在一個檔案裡的東西拆成真正的專案結構。


上一篇
Day21|四個 bug,三個不會報錯
下一篇
Day23|把滿血量改成負數,然後一筆假紀錄被永久存了下來
系列文
QA 的 JavaScript 初心之路:30 天做出一個能玩的戰鬥小遊戲《勇者 vs 小怪》 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言