Lesson 16 of 22, in world 5: Game Architecture
Events & the Observer Pattern
When the player grabs a coin, a lot should happen: the score goes up, a sound plays, sparkles fly, maybe an achievement unlocks. If the coin code calls all of those directly, it becomes tangled with every system in the game. Events (the observer pattern) decouple “something happened” from “who cares”.
Tight coupling
function onCoinTouched(coin) {
score += 1
hud.updateScore(score) // coin knows about the HUD
audio.play('coin') // …and audio
particles.burst(coin.x, coin.y) // …and particles
achievements.check(score) // …and achievements
// every new feature = edit this function
}Emit and subscribe
// The coin just announces what happened.
bus.emit('coin_collected', { x: coin.x, y: coin.y, value: 1 })
// Interested systems subscribe on their own, in their own files.
bus.on('coin_collected', e => score += e.value)
bus.on('coin_collected', () => audio.play('coin'))
bus.on('coin_collected', e => particles.burst(e.x, e.y))Collect coins with the arrow keys / WASD. The coin code does exactly one thing: bus.emit('coin_collected', …). It doesn't know who's listening. Switch listeners on and off — the coin code never changes.
When to use events — and when not
- Good: one-off happenings that several unrelated systems react to — died, collected, level complete, door opened, health changed.
- Good: child → parent communication (“button pressed”, “enemy died” to its spawner) without the child knowing its parent.
- Avoid: per-frame data (“player position changed” 60×/s). Just read the value directly.
- Avoid: long chains of events triggering events — they become hard to reason about.
Key takeaways
- Emitters announce what happened; listeners decide what to do about it.
- New features become new listeners — no edits to the emitter.
- Use events for discrete happenings, not per-frame data. Unsubscribe on destroy.
Check yourself
Q1. You want to add a "coins collected" statistic. With an event bus you…
Q2. Which is a poor fit for an event?
Q3. A destroyed enemy keeps reacting to "explosion" events. Why?
Exercises
- Turn all listeners off and collect a coin. Is the game "broken"? What does that tell you about coupling?
- Design the events a platformer level needs (start, checkpoint, death, …) and list who listens to each.
- Write a once(event, fn) helper that unsubscribes itself after the first call.