第 5 步:鬼會自己追¶
朋友回家了?沒關係,這一步讓鬼自己動腦追小精靈。你會寫出遊戲裡的第一個「AI」!

完整程式¶
| main.cpp | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | |
在專案資料夾的終端機編譯並執行(按 Up 鍵可以叫出上次的指令):
撐得過 30 秒嗎?
程式怎麼運作¶
鬼多久走一步¶
還記得第 1 步的 game.time() 嗎?ghost_next_move 記住「鬼下一次可以走的時間」。時間還沒到就 return;時間到了,就把下一次訂在 0.3 秒後,然後走一步。所以鬼每 0.3 秒走一格。
鬼的大腦¶
鬼每次要走的時候,會這樣想:
- 看看上、下、左、右四個方向。
- 跳過牆壁,也不要往回走。
- 算算看走過去之後離小精靈多遠,選最近的那個方向。
「多遠」是用橫向距離加上縱向距離來算的(std::abs 是絕對值)。kDX、kDY 兩個陣列記下每個方向要怎麼移動,例如方向 0(右)就是 kDX[0] = 1、kDY[0] = 0。
為什麼不能往回走?試試看把 if (d == back) continue; 刪掉,鬼會常常卡在牆角左右抖動。只有走進死路的時候,鬼才會回頭。
咦?鬼穿過去了?¶
你可能注意到,ChasePacman 在移動前和移動後都用 Caught 檢查了一次。明明第 4 步的碰撞函式就會抓到同一格的情況,為什麼還要自己檢查?
因為有一種情況,碰撞函式抓不到:小精靈和鬼面對面,在同一幀一起往對方走一格。
Grid++ 是在所有東西都走完之後才檢查碰撞。這時候兩者剛好交換位置,從來沒有站在同一格過,於是鬼就「穿過」小精靈了。
所以鬼要自己多看兩眼:
- 走之前:如果小精靈已經自己撞進鬼的格子,就是抓到了。
- 走之後:如果鬼撲進了小精靈的格子,也是抓到了。這時遊戲已經結束,小精靈就沒機會再逃走。
這樣不管小精靈和鬼誰先移動,都不會再穿過去。
親眼看看這個問題
把兩段 if (Caught(self)) ... 都刪掉,然後在走廊上正面迎向鬼,多試幾次。偶爾會看到鬼直接穿過小精靈!看完記得改回來。
試試看¶
- 把
0.3改成0.15,鬼會變得多可怕? - 讓鬼越來越快:每走一步,就把等待時間縮短一點點。
- 挑戰:再加一隻鬼,讓它隨機選方向,而不是追著小精靈。(提示:
game.random(0, 3)會隨機給你 0 到 3 的整數。第二隻鬼需要自己的ghost_direction和ghost_next_move。)
跑不起來,或結果和圖片不一樣?
先確認整份程式都有貼上、而且有存檔。還是不行的話,可以直接執行這一步的參考答案,看看哪裡不同:專案的 steps/ 資料夾裡有每一步的完整程式。
想知道更多?
完整版 Pac-Man 有四隻各有個性的鬼,每隻鬼都用 set() 和 get() 記住自己的方向和計時器。請看多隻鬼的獨立狀態。