Lesson 14 of 22, in world 5: Game Architecture
Scene Graphs & Entities
A real game has hundreds of objects: players, enemies, bullets, pickups, particles, UI. How do you organise them? Two ideas do most of the work: the scene graph (objects arranged in a tree) and entities built from components.
The scene graph
Objects are arranged as a tree. Each node stores a transform relative to its parent(lesson 4). A tank’s turret is a child of the tank body, so when the body drives and turns, the turret comes along for free — and the turret only has to worry about its own rotation.
The Sun spins; the Planet is its child at local (150, 0); the Moon is the Planet's child at local (45, 0). No code moves the Moon around the Sun — it inherits that from its parents. Reparent the Moon to see what changes.
// world transform = parent's world transform × my local transform
function worldMatrix(node) {
const local = translate(node.x, node.y).rotate(node.rotation).scale(node.scale)
return node.parent ? worldMatrix(node.parent).multiply(local) : local
}Entities and components
The naive way to model game objects is a deep class hierarchy: Entity → Character → Enemy → FlyingEnemy → FlyingShootingEnemy. It falls apart quickly (what about a walking shooting enemy?). Games prefer composition: an entity is a bag of small, reusable components.
const bat = {
transform: { x: 100, y: 50 },
velocity: { x: 0, y: 0 },
sprite: { sheet: 'bat.png', anim: 'fly' },
collider: { r: 8 },
health: { hp: 3 },
ai: { kind: 'chase', speed: 90 },
}
// Systems process every entity that has the components they need:
for (const e of entities) if (e.velocity) movementSystem(e, dt)
for (const e of entities) if (e.ai) aiSystem(e, dt)Entity lifecycle: spawning and destroying
Bullets, particles and enemies are created and destroyed constantly. Two practical rules:
- Don’t remove from a list while iterating it forwards. Mark entities as
deadduring update, then filter them out at the end of the frame (or iterate backwards). - Pool hot objects. Creating thousands of bullets per second triggers garbage collection hitches; reusing a fixed pool of inactive bullets avoids it.
for (const e of entities) e.update(dt) // may set e.dead = true
entities = entities.filter(e => !e.dead) // clean up once per frameKey takeaways
- A scene graph is a tree; children inherit their parent’s transform.
- world transform = parent world × local.
- Build entities by composing components, not deep inheritance.
- Remove dead entities after the update loop; pool objects that spawn often.
Check yourself
Q1. A gun is a child of the player at local (20, 0). The player moves to (300, 100), no rotation. Where is the gun in the world?
Q2. Why prefer components over a deep class hierarchy?
Q3. What’s the safe way to delete enemies that died during update?
Exercises
- Reparent the Moon to the Sun. Why does it now orbit the Sun at 45 px? Then set it to World — why does it stop moving?
- Draw the scene tree for a car with four wheels, a driver and a headlight.
- Describe a "spinning sawblade on a moving platform" as components. Which components does each entity need?