W3D5: Live coding Duck Invaders (4/4)
The final Duck Invaders session — boss fight, soundtrack stack manager, scoring, lives, weapon upgrade chains, a main menu and shipping-level scope cuts.
Lucian Lazar · 26 Jul 2024
Session four is where Duck Invaders stops being a pile of systems and becomes a game you can start, lose, win and quit. Lucian opens by listing what’s missing, decides what gets cut, and then spends the rest of the session filling the gaps: a boss with an animated arrival, a score, lives, a main menu, level transitions, and the weapon upgrade chain he’s been wanting since day one. If you’ve followed the other three live coding days, this is the payoff.
What’s covered
Scoping down to something shippable
- The opening ten minutes are a triage exercise. What does a game need to feel finished? Menu button, a boss, two levels (level two is a copy of level one, and Lucian is honest about that), lives, score, a congratulations panel.
- His reasoning for duplicating level one: if you know how to move from level 1 to level 2, you can have unlimited levels. Everything else is content work.
- Mobile is discussed and deprioritised. Auto-fire on mobile would simplify input,
Input.mousePositionalready works on touch, but it’s not happening this week.
Soundtrack manager
- Generates a boss track with a music AI, drops it in, then builds a
SoundtrackManagerthat keeps a stack of soundtracks. A wave can push its own track; when the wave dies it pops back to whatever was playing before. - Made a lazy singleton with
DontDestroyOnLoad, with a proper explanation of why: the temporary “DontDestroyOnLoad” scene exists so your audio doesn’t cut out mid scene load. - He also sketches the version he’d write if he had time: hand out a token when you push a track, so a caller can’t cancel someone else’s soundtrack.
Boss duck
- The boss is literally a giant sphere duck with 10,000 health. No apologies. “You have to not be distracted by details because we have priorities, we have to ship a game.”
- Animator controller with an arriving state that doesn’t loop feeding into a looping movement state, keyframed by hand in the animation window.
- Rotation gets pulled out of the animation and into a tiny
RotatingObjectscript, because blending rotation into a position clip is more trouble than it’s worth. - Animation events on the movement clip call
EnemySpawner.Burstto lay a row of eggs, and flip the rotation speed up and back down around the burst. - A health bar built from a world-space canvas and a single stretched image, with the pivot moved to x=0 so it drains left to right.
Progression, menu and UI
LevelManagergets anextLevelstring field and loads it withSceneManager.LoadScene. If there’s no next level, the game-complete panel shows instead.- A
LoadSceneOnClickcomponent wired throughbutton.onClick.AddListener— with the obligatory moment of forgetting you can’t+=a UnityEvent. - Main menu scene built from scratch: dark camera clear colour instead of fighting lighting settings, a Start button, a particle system moved into camera view with the align-view-to-camera trick.
- Scoring: a
Scoresingleton set inAwake, and every enemy carries the points it awards. Yellow 10, green 30, blue 200, overgrown 300, boss 100,000.
Weapon upgrades and lives
- Each weapon prefab points at its own upgraded variant. Pick up an upgrade power-up and the player just asks the current weapon for
nextUpgradeand hands it over. - Three upgrade tiers per weapon, each with a bigger visual, a bigger projectile, more damage and a lower-pitched fire sound. Tedious to author. He says so out loud and does it anyway.
- Weapons get a
tierfield so picking up a plain weapon power-up won’t downgrade you, unless it’s a different weapon type. - A
GameManagerwith five lives, heart icons in a horizontal layout group, respawn position, health reset, andDontDestroyOnLoadso it survives the level change. - Ducks damage the player on contact, with a one-second cooldown so you don’t lose three lives to a single collision.
Timestamps
- 00:03:00 — Feature triage: what’s left, what gets cut, and why level two is a copy of level one
- 00:12:00 — Walking the GDD, mobile input, and why auto-fire would simplify things
- 00:20:00 — Generating the boss soundtrack and copying the attribution
- 00:28:00 — Building
SoundtrackManageras a push/pop stack of tracks - 00:42:00 — Singleton pattern with
DontDestroyOnLoad, explained properly - 00:55:00 — Boss duck prefab: sphere visual, collider, scale, 10,000 health
- 01:08:00 — Animator controller with arriving and looping movement clips
- 01:22:00 — Pulling rotation out of the animation into a
RotatingObjectscript - 01:33:00 — Animation events driving the egg burst and rotation speed
- 01:48:00 — Boss health bar, pivot placement and colour by health
- 02:00:00 — Wave four with the boss, and the wave-level soundtrack override
- 02:10:00 —
nextLevel,SceneManager.LoadScene, and the game-complete panel - 02:25:00 — Main menu scene,
LoadSceneOnClick, particle background - 02:45:00 — Score singleton and per-enemy score values
- 03:00:00 — Death effects, screen shake, pitched-down boss death sound
- 03:12:00 — Weapon upgrade chain: prefabs, tiers,
nextUpgrade.GiveTo(player) - 03:55:00 —
GameManager, lives, heart icons and respawn - 04:35:00 — Contact damage with a one-second cooldown
- 04:50:00 — Rewriting player movement to use mouse delta, plus screen clamping
- 05:10:00 — Playtest, balancing pass, and fixing the egg burst spawn positions
What you build
By the end you have a playable loop: menu → level one → four waves → boss → win or lose screen → back to the menu. Score ticks up, hearts tick down, weapons get visibly and audibly stronger, and the boss sweeps across the screen laying rows of eggs at you.
Two things worth watching for beyond the features. First, the movement rewrite near the end: the player was teleporting to the cursor, which meant one flick of the mouse could drag you through three ducks. Switching to a mouse delta with Vector3.MoveTowards and a max speed fixes it. Lucian gets stuck on the screen-to-world conversion, asks an AI to fix it, and says the quiet part out loud: “do you want to look smart, or do you want to complete your game.”
Second, the debugging. Things break on stream. A lost particle system, a wrong scale on a prefab variant, eggs spawning from the wrong transform. You watch him find each one rather than cut to a working version.
// SoundtrackManager - push a new track, pop back when it's done
public void StackSoundtrack(GameObject trackPrefab)
{
if (currentSoundtrack != null)
{
currentSoundtrack.gameObject.SetActive(false);
soundtracksStack.Push(currentSoundtrack);
}
currentSoundtrack = Instantiate(trackPrefab, transform);
}
```csharp
// BaseDuck - hurt the player on contact, but not more than once per second
public void ApplyDamage(IDamageable damageable)
{
if (Time.time - lastTimeDamagedSomeone < 1f) return;
damageable.ReceiveDamage(touchDamage);
lastTimeDamagedSomeone = Time.time;
}
## Who should watch this
This is the session for anyone who has built half a game and never finished one. The technical content is solid — animator states, animation events, singletons across scene loads, prefab variants for upgrade chains — but the real lesson is the scope-cutting and the willingness to ship a boss that's a grey sphere. Watch it after the first three Duck Invaders days, or on its own if you want to see how a small game gets closed out under a deadline.
Part of the full curriculum. Course overview.