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

ScriptResponsibilityKey wiki concept
MazeGrid.csTile maze: walls, pellets, cell↔world, BFS distanceunity-tilemap, pathfinding-algorithms, csharp-collections
GridMover.csBase: glides actors cell-to-cell, asks subclass which waycsharp-inheritance
PacPlayer.csBuffered grid input; eats pellets on arrivalunity-input
Ghost.csScatter / Chase / Frightened FSM choosing a BFS targetai-state-machine-pattern
PacManGame.csPhase rhythm, power-pellet mode, score, lives, win/loseunity-gamemanager-pattern
ArcadeHud.cs (in ../shared/)Shared on-screen HUD: a status line plus a timed centre message, drawn with TextMeshProui-design, game-interface-types

How the three step-7 ideas show up

  • Tilemap. MazeGrid is a grid of walls and pellets with CellToWorld / 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.Distance is 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. Ghost has 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)

  1. New 2D scene, Main Camera set to Orthographic (size ~4).
  2. 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).
  3. Player prefab: a sprite with the PacPlayer script. Make it a prefab.
  4. Ghost prefab: a sprite with the Ghost script. Make it a prefab.
  5. Maze: empty GameObject Maze at the origin, add MazeGrid, and assign the Wall / Pellet / Power Pellet prefabs. Width and height should be odd (defaults 15 × 11).
  6. Game: empty GameObject Game, add PacManGame, assign the Maze, the Player Prefab, and the Ghost Prefab.
  7. HUD: copy ../shared/ArcadeHud.cs into the project as well. Add GameObject → UI → Text - TextMeshPro (click Import TMP Essentials the first time). Unity also creates a Canvas (leave it on Screen Space - Overlay) and an EventSystem. Name the text StatusText and anchor it to the top-left; duplicate it as MessageText, anchor it to the centre, and set its alignment to centre. Add ArcadeHud to the Canvas, drag both texts into its Status Text and Message Text fields, then drag the Canvas into the Hud field on Game. (Leave Hud empty and messages print to the Console instead.)
  8. 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 and lives show on screen.

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

  1. Four ghost personalities — give each ghost a different Chase target (ahead of the player, a mirror of another ghost, etc.) as the original does.
  2. A pathfinding* — swap the BFS for A* from pathfinding-algorithms and compare on a larger maze.
  3. Author the maze — feed MazeGrid a string[] layout (or a real unity-tilemap) instead of generating pillars, and parse #/./o/P/G.
  4. Frightened polish — slow the ghosts and flash them near the end of the timer; classic game-feel cues.

Source files

5 items under this folder.