第 3 步:迷宮
沒有牆的 Pac-Man 太好走了。這一步要蓋一座迷宮,而且我們的地圖是用文字畫出來的。

完整程式
| main.cpp |
|---|
| // 第 3 步:迷宮——撞到牆就走不過去。
#include "GridPlusPlus.h"
using gridpp::GameEngine;
using gridpp::GridObject;
using gridpp::Maze;
const int kCols = 15;
const int kRows = 11;
const char* kMap[kRows] = {
"###############",
"#P............#",
"#.#.###.###.#.#",
"#.............#",
"#.#.#.#.#.#.#.#",
"#.............#",
"#.#.#.#.#.#.#.#",
"#.............#",
"#.#.###.###.#.#",
"#............G#",
"###############",
}; // '#' 是牆,'P' 是小精靈的起點,'G' 是鬼的起點
Maze maze;
// 讓 who 往 (dx, dy) 走一格;前面是牆就不動。
void Walk(GridObject who, int dx, int dy) {
if (maze.isWall(who.x() + dx, who.y() + dy)) return;
who.move(dx, dy);
}
void MovePacman(GameEngine game, GridObject self) {
if (game.keyPressed(KEY_RIGHT)) {
self.setDirection(0);
Walk(self, 1, 0);
}
if (game.keyPressed(KEY_UP)) {
self.setDirection(1);
Walk(self, 0, -1);
}
if (game.keyPressed(KEY_LEFT)) {
self.setDirection(2);
Walk(self, -1, 0);
}
if (game.keyPressed(KEY_DOWN)) {
self.setDirection(3);
Walk(self, 0, 1);
}
}
int main() {
GameEngine game(kCols, kRows, 48);
game.loadAssets("pacman.db");
game.setBackgroundColor(BLACK);
maze = game.addMaze(kCols, kRows);
maze.setWallImages("wall_iso", "wall_end", "wall_straight", "wall_corner", "wall_tee", "wall_cross");
for (int y = 0; y < kRows; ++y) {
for (int x = 0; x < kCols; ++x) {
if (kMap[y][x] == '#') maze.setWall(x, y);
}
}
GridObject pacman = game.addObject("pacman", nullptr, MovePacman);
pacman.setPosition(1, 1);
game.run();
return 0;
}
|
在專案資料夾的終端機編譯並執行(按 Up 鍵可以叫出上次的指令):
程式怎麼運作
用文字畫地圖
const char* kMap[kRows] = {
"###############",
"#P............#",
...
};
這是一個字串陣列,每個字串是地圖的一列,每個字元是一格:# 是牆,其他都是通道。kMap[y][x] 就是第 y 列、第 x 欄的那一格。.、P、G 現在還用不到,之後的步驟會派上用場。
把牆蓋起來
for (int y = 0; y < kRows; ++y) {
for (int x = 0; x < kCols; ++x) {
if (kMap[y][x] == '#') maze.setWall(x, y);
}
}
兩層迴圈走過地圖的每一格,看到 # 就呼叫 maze.setWall(x, y) 在那裡蓋牆。setWallImages 會依照每面牆和隔壁牆的相連方式,自動選出直線、轉角或交叉的圖片,所以牆壁會連成漂亮的形狀。
撞牆就停下來
void Walk(GridObject who, int dx, int dy) {
if (maze.isWall(who.x() + dx, who.y() + dy)) return;
who.move(dx, dy);
}
走之前先算出下一格的座標,用 maze.isWall 問問看那裡是不是牆。是牆就 return,不移動。我們把這段寫成 Walk 函式,因為下一步的鬼也會用到它。
試試看
- 改改看
kMap,畫一座你自己的迷宮。(注意:每一列都要剛好 15 個字元,最外圈最好都是牆。)
- 把
48 改成 32 或 64,整個畫面會怎麼變?
- 挑戰:在地圖中間挖一條「祕密通道」,讓小精靈從左邊牆壁的洞穿出去,從右邊出來。(提示:先把左右兩邊同一列的
# 改成 .,再在 Walk 裡判斷 x 超出範圍的情況。)
跑不起來,或結果和圖片不一樣?
先確認整份程式都有貼上、而且有存檔。還是不行的話,可以直接執行這一步的參考答案,看看哪裡不同:專案的 steps/ 資料夾裡有每一步的完整程式。
想知道更多?
Maze 的更多用法,例如從檔案讀取地圖,請看地圖與迷宮。
第 4 步:鬼來了