Game Dev 101
Progress
0/22

Lesson 16 of 22, in world 5: Game Architecture

Events & the Observer Pattern

Time
About 15 min
Builds on
Collision DetectionHandling InputScene Graphs & Entities

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

JavaScript
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

JavaScript
// 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))
LiveOne event, many listeners

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

  1. Turn all listeners off and collect a coin. Is the game "broken"? What does that tell you about coupling?
  2. Design the events a platformer level needs (start, checkpoint, death, …) and list who listens to each.
  3. Write a once(event, fn) helper that unsubscribes itself after the first call.