Minimal Frogger (Unity / C#)
The step-6 project in overview-arcade-classics-as-learning-projects. It returns to discrete grid movement (like Snake) but sets it against a world that moves continuously, which is what makes timing the core skill. It covers the canon reference’s list for Frogger (see source-classic-games-canon): grid movement, moving hazards, timing, and level boundaries.
Original project inspired by a recognisable pattern — not a clone.
Files
| Script | Responsibility | Key wiki concept |
|---|---|---|
Hazard.cs | A car or log that slides at fixed velocity and despawns off-screen | unity-transform |
Lane.cs | Spawns hazards from one edge on a timer | monobehaviour-lifecycle |
FrogController.cs | Discrete hop + boundary clamp; squash / drown / ride logic | unity-input, unity-collider2d-and-triggers |
FroggerGame.cs | Builds rows from data, maps height → lane type, owns score/lives | csharp-enums, unity-gamemanager-pattern |
What is new at this step
- Grid hop against continuous motion. The frog moves one row/column per key
press (
GetKeyDown), but cars and logs move every frame. The interesting decisions are when to hop, not just where — that is the timing skill. - Two hazard rules from one structure. A Road lane kills on contact; a
River lane kills unless the frog is on a log, which then carries it. Both
are just lanes with a type; the frog resolves the rule each frame in
EvaluateLane. - Data-driven layout.
FroggerGamebuilds the board from aLaneDef[]you fill in the Inspector (type, speed, spawn interval, bottom row up). The safe start and goal rows are added automatically. - Level boundaries. The frog is clamped horizontally, and being carried off the screen edge by a log is fatal — boundaries are part of the challenge.
Scene setup (about 12 minutes)
- New 2D scene, Main Camera set to Orthographic (size ~5).
- Add two user layers:
VehicleandLog(Edit → Project Settings → Tags and Layers). - Car prefab: a square/rectangle sprite, layer Vehicle, with a Box
Collider 2D and the
Hazardscript. Make it a prefab; delete the scene copy. - Log prefab: a wide sprite (scale x to ~2.5), layer Log, with a
Box Collider 2D and the
Hazardscript. Make it a prefab. - Frog: a small sprite at the origin with the
FrogControllerscript. Set Vehicle Mask to the Vehicle layer and Log Mask to the Log layer. (No collider needed — it uses overlap queries.) - Game: empty GameObject
Game, addFroggerGame, assign the Car Prefab, Log Prefab, and Frog, then fill Lanes bottom-up, e.g.:- Road, speed
3, interval1.6 - Road, speed
-4, interval1.3 - Road, speed
2.5, interval1.8 - Safe,
0,0(a median strip) - River, speed
2, interval2.0 - River, speed
-2.5, interval2.2
- Road, speed
- Press Play. WASD or arrows hop one cell at a time. Cross the roads, ride the logs across the river, and reach the top row to score. Getting squashed, drowning, or drifting off-screen costs a life.
Why timing is the whole game
Snake’s grid asked “which way?“. Frogger’s grid asks “which way, and when?“. Because the frog is discrete and the traffic is continuous, every safe move depends on reading gaps and committing at the right moment. Tuning lane speeds and spawn intervals is tuning the difficulty directly — see game-feel and smooth-learning-curves.
Extension ladder
These point toward step 7 (Pac-Man-lite: tilemaps, pathfinding, AI state machines):
- On-screen score and lives — replace
Debug.Logwith UI/TMP text. - Home slots — several goal bays at the top that each fill once; win when all are filled (a small win-condition system).
- Snap to a tile grid — round the frog’s x to fixed columns and lay lanes out with unity-tilemap; the bridge to maze games.
- Pool the hazards — reuse the object-pool-pattern from step 4 instead of Instantiate/Destroy.
- Timer pressure and turtles that submerge — add a countdown and periodic “logs” that sink, deepening the timing read.