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:0sorts before2: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.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…