Minimal Space Invaders (Unity / C#)
The step-4 project in overview-arcade-classics-as-learning-projects. It is the first project in the route with multiple interacting systems, and it introduces the four skills the canon reference (see source-classic-games-canon) lists for Space Invaders: enemy formations, timers, projectile pooling, and state changes (plus lives).
Original project inspired by a recognisable pattern — not a clone.
Files
| Script | Responsibility | Key wiki concept |
|---|---|---|
IHittable.cs | One-method interface so a bullet can damage anything | csharp-interfaces |
ProjectilePool.cs | Reuses bullets instead of Instantiate/Destroy per shot | object-pool-pattern |
Projectile.cs | Pooled, transform-driven bullet; hits a target layer, returns itself | unity-collider2d-and-triggers |
PlayerShip.cs | Move + fire on a cooldown timer; takes a hit | unity-input |
Invader.cs | One formation member; dies on hit, reports to the formation | csharp-interfaces |
InvaderFormation.cs | Marches the grid, edge-drops, fires on a timer, speeds up per kill | csharp-collections |
SpaceInvadersGame.cs | Owns score, lives, and the State machine; wave/lose flow | unity-gamemanager-pattern, csharp-enums |
What is new at this step
- Enemy formation. Every invader is a child of
InvaderFormation, so the whole grid marches by moving one transform. The formation reverses and drops a row when its edge reaches the screen, and it tracks survivors in aList. - Timers. Two independent timers drive the game: one steps the march, one
fires from a random survivor. The player’s fire rate is a third cooldown timer.
All are frame-rate independent (accumulating
Time.deltaTime). - Projectile pooling. Both sides share one
ProjectilePool, firing the same prefab parameterised by direction and target layer. This is the route’s first real use of object-pool-pattern. - State changes.
SpaceInvadersGameholds aStateenum and routes every event (invader killed, wave cleared, player hit, invaders landed) through it.
Scene setup (about 15 minutes)
- New 2D scene, Main Camera set to Orthographic.
- Add two user layers:
PlayerandInvaders(Edit → Project Settings → Tags and Layers). - Bullet prefab: a small thin square sprite, add a Box Collider 2D and the
Projectilescript. Make it a prefab; delete the scene copy. (It needs no layer of its own — it targets a layer.) - Pool: empty GameObject
BulletPool, addProjectilePool, assign the bullet prefab. - Player: a square sprite near the bottom, layer Player, with a Box
Collider 2D and the
PlayerShipscript. Assign Bullet Pool, and set Invader Mask to the Invaders layer. - Invader prefab: a square sprite, layer Invaders, with a Box Collider
2D and the
Invaderscript. Make it a prefab; delete the scene copy. - Formation: empty GameObject
Formation, addInvaderFormation, assign the invader prefab and the Bullet Pool, and set Player Mask to the Player layer. - Game: empty GameObject
Game, addSpaceInvadersGame, and drag in thePlayerandFormation. - Press Play. Move with A/D or arrows, Space to fire. Clearing the wave starts a faster one; losing all lives restarts. Score, lives, and waves print to the Console.
Why pooling here
A wave can put dozens of bullets on screen per second. Instantiating and
destroying each one churns memory and can cause GC hitches — exactly the case
object-pool-pattern is built for. Swapping the pool for plain
Instantiate/Destroy and watching the unity-profiler is a good exercise.
Extension ladder
These lead toward step 5 (Asteroids, vector movement and wrapping):
- On-screen score, lives, and wave — replace
Debug.Logwith UI/TMP text. - Destructible shields — bunkers that erode from both bullet directions (reuses Breakout’s brick idea).
- Animation frames — alternate the invader sprite each march step for the classic shuffle; first touch of unity-animator-scripting.
- Dive attacks — occasionally peel an invader out of formation to swoop (the bridge to Galaga and to steering-behaviours).
- Audio and hit feedback — the rising march tempo is iconic game-feel; see unity-audiosource.