Minimal Pac-Man-lite (Unity / C#)
The step-7 project in overview-arcade-classics-as-learning-projects and the capstone of the route. It is the first project with real AI, and it brings together the skills the canon reference (see source-classic-games-canon) lists for Pac-Man: tilemaps, simple pathfinding, finite state machines, and pickups.
Original project inspired by a recognisable pattern — not a clone.
Files
| Script | Responsibility | Key wiki concept |
|---|---|---|
MazeGrid.cs | Tile maze: walls, pellets, cell↔world, BFS distance | unity-tilemap, pathfinding-algorithms, csharp-collections |
GridMover.cs | Base: glides actors cell-to-cell, asks subclass which way | csharp-inheritance |
PacPlayer.cs | Buffered grid input; eats pellets on arrival | unity-input |
Ghost.cs | Scatter / Chase / Frightened FSM choosing a BFS target | ai-state-machine-pattern |
PacManGame.cs | Phase rhythm, power-pellet mode, score, lives, win/lose | unity-gamemanager-pattern |
How the three step-7 ideas show up
- Tilemap.
MazeGridis a grid of walls and pellets withCellToWorld/IsWalkable. It is generated as a connected “pillar” maze so it is always solvable; the README below shows how to swap in a hand-authored layout or a real unity-tilemap. - Pathfinding.
MazeGrid.Distanceis a breadth-first search. A ghost looks at its walkable neighbours and steps toward the one with the best BFS distance to its target — simple, but genuine pathfinding. For bigger maps this is where A* on pathfinding-algorithms takes over. - Finite state machine.
Ghosthas three states. Each picks a different target (Chase → the player, Scatter → a home corner, Frightened → away from the player), while the movement code stays identical. That separation of “what do I want?” from “how do I move?” is the heart of ai-state-machine-pattern.
GridMover is shared by the player and the ghosts via csharp-inheritance:
the gliding-between-cells mechanism is written once, and only the per-cell
decision differs (read input vs run the FSM).
Scene setup (about 12 minutes)
- New 2D scene, Main Camera set to Orthographic (size ~4).
- Tile prefabs — four square sprites, each dragged into the Project window as
a prefab (delete the scene copies). No scripts or colliders needed:
Wall(solid colour),Pellet(small dot),PowerPellet(bigger dot).
- Player prefab: a sprite with the
PacPlayerscript. Make it a prefab. - Ghost prefab: a sprite with the
Ghostscript. Make it a prefab. - Maze: empty GameObject
Mazeat the origin, addMazeGrid, and assign the Wall / Pellet / Power Pellet prefabs. Width and height should be odd (defaults 15 × 11). - Game: empty GameObject
Game, addPacManGame, assign the Maze, the Player Prefab, and the Ghost Prefab. - Press Play. WASD or arrows steer; eat all pellets to clear the level. A power pellet turns the ghosts blue (Frightened) so you can send them home for bonus points; otherwise a ghost costs a life. Score, lives, and pellet count print to the Console.
The player and ghosts are spawned at the markers MazeGrid chooses, so you do not
place them in the scene yourself.
Why this is the capstone
Every earlier step fed into this one: grid movement (Snake), timed phases
(Space Invaders), state ownership (the game controllers), and now pathfinding and
a state machine driving an opponent that reacts to you. Tuning scatterTime,
chaseTime, frightenedTime, and the ghost speed changes the whole feel of the
hunt — see game-feel and smooth-learning-curves.
Extension ladder
- On-screen score and lives — replace
Debug.Logwith UI/TMP text. - Four ghost personalities — give each ghost a different Chase target (ahead of the player, a mirror of another ghost, etc.) as the original does.
- A pathfinding* — swap the BFS for A* from pathfinding-algorithms and compare on a larger maze.
- Author the maze — feed
MazeGridastring[]layout (or a real unity-tilemap) instead of generating pillars, and parse#/./o/P/G. - Frightened polish — slow the ghosts and flash them near the end of the timer; classic game-feel cues.