Skip to content
Lesson 5 of 14

Step 1 of 5 · Reading · ~3 min

Read

Entity-Component System

Entity-Component System (ECS)

An ECS stores a game's objects as plain data in tables instead of as a tree of classes. After this lesson your program will hold a world of entities with components attached, hand out handles that notice when an entity has been destroyed, and answer "which entities have all of these components?".

Composition instead of inheritance

A class hierarchy needs a class for every combination: Player, FlyingPlayer, PoisonedFlyingPlayer. An ECS has three parts instead:

  • an entity is only an identity;
  • a component is a small piece of plain data: Position, Velocity, Health;
  • a system is logic that runs over every entity that has a given set of components (the next lesson).

A "player" is whatever entity happens to carry Position, Velocity, Health and Input. Making it poisoned means adding a Poisoned component at runtime, with no new class.

An entity is a handle, not just a number

The obvious identity is a slot number into arrays. It breaks the moment slots are reused. Suppose a homing missile stores the id of its target, enemy 7. The enemy dies, slot 7 is recycled for a health pickup, and the missile now chases a health pickup. Nothing crashed; the bug is silent.

The fix is a generation. Every slot has a counter that goes up each time the entity in it is destroyed, and a handle is the pair slot:generation. A handle is live only if its slot is in use and the generation matches. Old handles then fail loudly instead of aliasing a stranger:

SPAWN            -> 0:0
SPAWN            -> 1:0
DESPAWN 0:0      -> ok        slot 0 is free, its generation becomes 1
SPAWN            -> 0:1       slot 0 is reused, with the new generation
GET 0:0 Hp       -> stale     the old handle is refused

Free slots are kept on a stack, so the slot freed most recently is reused first. A recycled slot must start empty: destroying an entity removes all its components.

Where the components live

Three layouts, in increasing order of speed:

  • A table per entity: simple, but every system chases pointers.
  • A sparse set per component type: a dense array of values plus an index from slot to position. Lookup is constant time and iterating one component is a straight scan.
  • Archetypes: entities with exactly the same set of component types share one table with a column per type. Iteration touches contiguous memory, which is why Bevy, flecs and Unity DOTS are fast. Adding or removing a component moves the entity between tables.

Queries

A query is "all live entities with A and B". It is an intersection: walk the smallest component set and test the others. The result order must be deterministic, because replays and lockstep networking depend on it; here it is ascending by slot number, compared as numbers.

Engines also forbid creating or destroying entities in the middle of a query. Those changes go into a command buffer that is applied between systems.

Common mistakes

  • Reusing a slot without bumping its generation.
  • Letting a recycled entity inherit the previous owner's components.
  • Sorting handles as text, so 10:0 sorts before 2:0.
  • Treating a stale handle as an error that stops the program. Asking about a dead entity is normal; answer stale.
  • Reusing the oldest freed slot instead of the newest.

Your exercise

Build the world described in the exercise. The starter parses commands and prints results; you write the six world operations. The most likely mistake is forgetting to clear components when a slot is freed.

Up nextSystems & SchedulesEntity-Component System

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…