跳轉到

多隻鬼的獨立狀態

四隻鬼使用相同的移動程式,但每隻都要記住自己的方向、移動計時器、顏色與策略。Ghost class 讓每次建立 instance 時同時建立一份 timer_direction_random_。這正是第 5 章從多隻地鼠遇到的共享狀態問題所引出的設計。

class Ghost : public GridObject {
public:
    Ghost(int x, int y, Color color, bool is_random)
        : GridObject("ghost", x, y), random_(is_random) {
        set_tag("ghost");
        set_tint(color);
    }

    void OnUpdate() override {
        if (g_state != 1 || g_paused || g_player == nullptr) return;
        if (++timer_ < 12) return;
        timer_ = 0;

        // Choose a legal direction, then move one cell.
    }

private:
    bool random_;
    int timer_ = 0;
    int direction_ = 0;
};

玩家每 8 幀移動,鬼每 12 幀移動。每隻鬼各自累加 timer_,互不重設其他 instance 的計時器。set_tint(color) 讓四隻鬼共用同一張白色素材,再以不同顏色繪製。

收集合法方向

鬼每次準備移動時檢查四個相鄰格。any 保存所有非牆方向;options 再排除目前方向的反向。只有走到死路、options 為空時,才允許從 any 回頭。

int options[4];
int option_count = 0;
int any[4];
int any_count = 0;

for (int direction = 0; direction < 4; ++direction) {
    if (g_maze->IsWall(x() + kDirectionX[direction],
                       y() + kDirectionY[direction])) {
        continue;
    }

    any[any_count++] = direction;
    if (direction == (direction_ ^ 2)) continue;
    options[option_count++] = direction;
}

int* pool = option_count > 0 ? options : any;
const int pool_count = option_count > 0 ? option_count : any_count;
if (pool_count == 0) return;

方向編號 0、1、2、3 對應右、上、左、下。direction_ ^ 2 會把右和左、上和下互換,得到反向方向。若四周全是牆,pool_count 為 0,鬼保留原位。

隨機與追蹤策略

第四隻鬼在合法方向中隨機選擇。其餘鬼計算每個候選格到玩家的曼哈頓距離,選擇距離最小的方向。

int pick = pool[0];
if (random_) {
    pick = pool[GetRandomValue(0, pool_count - 1)];
} else {
    int best = 1 << 30;
    for (int i = 0; i < pool_count; ++i) {
        const int direction = pool[i];
        const int distance = ManhattanDistance(
            x() + kDirectionX[direction],
            y() + kDirectionY[direction],
            g_player->x(),
            g_player->y()
        );
        if (distance < best) {
            best = distance;
            pick = direction;
        }
    }
}

direction_ = pick;
Move(kDirectionX[direction_], kDirectionY[direction_]);

g_player 是 Engine 擁有玩家物件的借用指標。Ghost 只讀取座標,不釋放也不保存所有權。重新建立關卡時,BuildLevel() 先把它設為 nullptr,再指向新玩家。

建立不同的鬼

地圖中的每個 3 tile 生成一隻 Ghost。顏色依生成順序循環,第四隻使用隨機策略。

const Color colors[4] = {RED, PINK, SKYBLUE, ORANGE};
int ghost_count = 0;

if (tile == 3) {
    game.Spawn(new Ghost(
        x,
        y,
        colors[ghost_count % 4],
        ghost_count == 3
    ));
    ++ghost_count;
}

Summary

  • 每個 Ghost instance 保存自己的方向、計時器與策略。
  • IsWall() 在移動前排除牆面和地圖外座標。
  • 鬼優先避免回頭,死路時允許反向移動。
  • tint 讓多個物件以不同顏色共用同一素材。