Lesson 21 of 22, in world 7: Polish & Shipping
Capstone: Crystal Caves
Time to put it all together. Crystal Caves is a complete little game — title screen, a procedurally generated level, enemies with AI, shooting, juice, a HUD, win and lose states — built from nothing but the concepts in this course. Play it, then read how every lesson shows up.
Move with WASD or arrows, aim with the mouse, hold the mouse button to shoot, P to pause. Every run generates a brand-new cave. Turn your sound on!
Where every lesson lives in this game
| Lesson | How Crystal Caves uses it |
|---|---|
| 1 · What makes a game | Goal (collect 10 crystals), challenge (slimes, limited health), feedback, reward (score, best). |
| 2 · Game loop | update() then draw(), every frame, via the shared engine. |
| 3 · Delta time | Gameplay runs in a fixed 1/120 s step with an accumulator; particles and camera use variable dt. |
| 4 · Transforms | The gun is drawn by translating to the player and rotating to the aim angle. |
| 5 · Vectors | Normalized input, knockback directions, enemy-to-player vectors. |
| 6 · Velocity | Player and slimes accelerate toward a target velocity; recoil pushes velocity back. |
| 7 · Input | Held keys for movement, held mouse for auto-fire, pressed keys for start/pause. |
| 8 · Trig | atan2 aiming, cos/sin bullet velocity, sin bobbing crystals. |
| 9 · Collision detection | Circle-vs-circle for bullets, enemies, crystals and the player. |
| 10 · Collision response | Move one axis at a time and stop at walls. |
| 11 · Sprites | Slimes are a 2-frame pixel-art sprite sheet drawn with nearest-neighbour scaling. |
| 12 · Tilemaps | The cave is a 64×44 tile grid; collisions are tile lookups. |
| 13 · Camera | Smooth follow, clamped to the map; mouse converted from screen to world; minimap. |
| 14 · Entities | Bullets, enemies, particles are spawned and filtered out when dead. |
| 15 · State machines | Slimes switch wander ↔ chase; the game switches title / playing / paused / won / lost. |
| 16 · Events | enemy_killed, gem_collected, player_hit trigger sounds, particles, shake, score popups. |
| 17 · Procedural generation | Cellular-automata caves with a random seed every run. |
| 18 · AI & pathfinding | Flood fill keeps the cave connected; a BFS distance field (refreshed 4×/s) lets every slime path to you. |
| 19 · Game feel | Screen shake, hit-stop, particles, hit flash, recoil, knockback, sounds, invulnerability blink. |
| 20 · States, UI & audio | Title/pause/end screens, HUD with hearts, Web Audio blips, best score saved. |
Where to go from here
The best way to learn game development is to finish small games. Classic first projects, roughly in order:
- Pong — loop, input, collisions, score.
- Breakout — many entities, bounce response, levels as data.
- Flappy Bird — gravity, spawning, juice.
- Snake — grid logic, game states.
- Asteroids — vectors, rotation, wrap-around, particles.
- A one-screen platformer — tilemap, platformer physics, sprites, state machine.
- A top-down dungeon — camera, AI, pathfinding, procedural levels (like this one).
Key takeaways
- A full game is just the concepts you already know, composed together.
- Keep gameplay in a fixed-step update, presentation in variable-dt code, and connect them with events.
- Next: rebuild these ideas in a real engine — the final lesson maps everything to Godot.
Exercises
- Add a new enemy type that keeps its distance and shoots (flee when close, seek when far).
- Add a dash on Shift with a cooldown, using a state machine for the player.
- Add a health pickup that spawns via a weighted random table when slimes die.
- Change the generator so that crystals spawn in dead ends (tiles with only one floor neighbour).