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

ScriptResponsibilityKey wiki concept
ScreenWrap2D.csReusable: wraps any object to the opposite edgeunity-transform
ShipController.csRotate, thrust into a velocity vector, drag, fire, collideunity-input, steering-behaviours
Bullet.csShort-lived projectile; hits an asteroid and triggers its splitunity-collider2d-and-triggers
Asteroid.csDrifts, carries a Size tier, reports hitscsharp-enums
AsteroidsGame.csSpawns waves, performs the split, owns score/lives/statecsharp-collections, unity-gamemanager-pattern

What is new at this step

  • Inertial vector movement. Thrust adds to a velocity vector 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. ScreenWrap2D is written once as its own component and reused on the ship, the asteroids, and (optionally) the bullets. Wrapping in LateUpdate means it runs after movement each frame.
  • Spawning and recursive splitting. AsteroidsGame.OnAsteroidHit is 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)

  1. New 2D scene, Main Camera set to Orthographic.
  2. Add a user layer Asteroids (Edit → Project Settings → Tags and Layers).
  3. Asteroid prefab: a roughly round sprite (a square is fine), layer Asteroids, with a Circle Collider 2D, the Asteroid script, and a ScreenWrap2D component. Make it a prefab; delete the scene copy.
  4. Bullet prefab: a small dot sprite with the Bullet script. (Add ScreenWrap2D too if you want wrapping shots.) Make it a prefab.
  5. Ship: a triangle-ish sprite pointing up, at the origin. Add ShipController and ScreenWrap2D. 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.)
  6. Game: empty GameObject Game, add AsteroidsGame, and assign the Asteroid Prefab and the Ship.
  7. 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):

  1. On-screen score and lives — replace Debug.Log with UI/TMP text.
  2. Pool the bullets and asteroids — swap Instantiate/Destroy for the object-pool-pattern from step 4 and check the unity-profiler.
  3. Thrust visuals and screen shake — exhaust flame, fire recoil; minimal game-feel.
  4. Saucer enemy — a periodic UFO that crosses and shoots back (timer-driven, like the invader firing in step 4).
  5. Momentum-aware shots — add the ship’s velocity to the bullet velocity and feel how it changes aiming.

Source files

5 items under this folder.