Minimal Asteroids (Unity / C#)
The step-5 project in overview-arcade-classics-as-learning-projects, and the first one where the player’s craft has momentum rather than direct control. It introduces the skills the canon reference (see source-classic-games-canon) lists for Asteroids: vector movement, screen wrapping, spawning, and object splitting (plus inertia).
Original project inspired by a recognisable pattern — not a clone.
Files
| Script | Responsibility | Key wiki concept |
|---|---|---|
ScreenWrap2D.cs | Reusable: wraps any object to the opposite edge | unity-transform |
ShipController.cs | Rotate, thrust into a velocity vector, drag, fire, collide | unity-input, steering-behaviours |
Bullet.cs | Short-lived projectile; hits an asteroid and triggers its split | unity-collider2d-and-triggers |
Asteroid.cs | Drifts, carries a Size tier, reports hits | csharp-enums |
AsteroidsGame.cs | Spawns waves, performs the split, owns score/lives/state | csharp-collections, unity-gamemanager-pattern |
What is new at this step
- Inertial vector movement. Thrust adds to a
velocityvector instead of setting position. The ship keeps drifting after you let go, and turning only changes where thrust will push next — not where you are already heading. This is the same vector-integration idea behind steering-behaviours, stripped to its simplest form. - Screen wrapping.
ScreenWrap2Dis written once as its own component and reused on the ship, the asteroids, and (optionally) the bullets. Wrapping inLateUpdatemeans it runs after movement each frame. - Spawning and recursive splitting.
AsteroidsGame.OnAsteroidHitis the highlight: a Large asteroid becomes two Mediums, each Medium becomes two Smalls, and Smalls simply vanish. Centralising the spawn in the field keeps the recursion in one readable place.
Scene setup (about 12 minutes)
- New 2D scene, Main Camera set to Orthographic.
- Add a user layer
Asteroids(Edit → Project Settings → Tags and Layers). - Asteroid prefab: a roughly round sprite (a square is fine), layer
Asteroids, with a Circle Collider 2D, the
Asteroidscript, and aScreenWrap2Dcomponent. Make it a prefab; delete the scene copy. - Bullet prefab: a small dot sprite with the
Bulletscript. (AddScreenWrap2Dtoo if you want wrapping shots.) Make it a prefab. - Ship: a triangle-ish sprite pointing up, at the origin. Add
ShipControllerandScreenWrap2D. Assign the Bullet Prefab and set Asteroid Mask to the Asteroids layer. (The ship needs no collider — it checks for asteroids with an overlap query.) - Game: empty GameObject
Game, addAsteroidsGame, and assign the Asteroid Prefab and the Ship. - Press Play. A/D or left/right rotate, W/up thrusts, Space fires. Shooting a large rock splits it; clearing the field starts a busier wave; colliding with a rock costs a life (with a brief invulnerable respawn).
Why momentum changes the design
Direct-control games (Pong, Snake) ask “where do I want to be?“. Asteroids asks
“what is my velocity, and how do I manage it?“. That single change — integrating a
velocity vector instead of setting position — is why the ship feels so different
and why aiming while drifting is the core skill. It is worth tuning
thrustPower, maxSpeed, and drag to feel the trade-off; see game-feel.
Extension ladder
These point toward step 6 (Frogger) and step 7 (Pac-Man-lite):
- On-screen score and lives — replace
Debug.Logwith UI/TMP text. - Pool the bullets and asteroids — swap Instantiate/Destroy for the object-pool-pattern from step 4 and check the unity-profiler.
- Thrust visuals and screen shake — exhaust flame, fire recoil; minimal game-feel.
- Saucer enemy — a periodic UFO that crosses and shoots back (timer-driven, like the invader firing in step 4).
- Momentum-aware shots — add the ship’s velocity to the bullet velocity and feel how it changes aiming.