ECS theory
An ECS separates identity, data and behavior. SIECS is an archetype ECS: it groups entities with the same component set into the same table.
Identity is not data
Section titled “Identity is not data”An entity is a small handle. It does not contain Position, Health, a name,
or a virtual interface. Those are components or relations attached to the
handle.
ecs_entity_t ship = ecs_new();ecs_set(ship, Position, { 10.0f, 20.0f });ecs_set(ship, Health, { 100 });auto ship = ecs::entity::create() .set(Position{ 10.0f, 20.0f }) .set(Health{ 100 });The handle can be passed cheaply. Its generation prevents an old handle from becoming valid again when an entity index is reused.
Components define a table
Section titled “Components define a table”The component set is the entity’s archetype. These entities occupy different tables because their data shape differs:
| Entity | Components | Table |
|---|---|---|
| ship | Position, Health |
Position + Health |
| asteroid | Position |
Position |
| enemy | Position, Velocity, Health |
Position + Velocity + Health |
Within a table, SIECS stores one contiguous column per component. A system that
matches Position + Velocity receives contiguous arrays for both components.
That is the normal hot path.
Queries select tables, then batches
Section titled “Queries select tables, then batches”A query is not a per-entity lookup loop. It first selects matching tables, then iterates each table in batches.
ecs_query_id_t moving = ecs_query({ .components = { ecs_inout(Position), ecs_in(Velocity) },});
ecs_iter_t it = ecs_query_iter(moving);while (ecs_iter_next(&it)) { Position *position = ecs_field(&it, 0); const Velocity *velocity = ecs_field(&it, 1); for (uint32_t i = 0; i < it.count; i++) { position[i].x += velocity[i].x; }}ecs::query() .require<Position>() .require<Velocity>() .each([](Position &position, const Velocity &velocity) { position.x += velocity.x; });The query does not match the Position-only asteroid table. It does match the
enemy table, and it processes all matching rows with linear memory access.
Systems are scheduled queries
Section titled “Systems are scheduled queries”A system owns a persistent query and runs it in a phase. Use systems for work that happens repeatedly, such as simulation, transform propagation or render preparation. Use a standalone query for an explicit one-off scan.
world -> phase -> system -> matching table batch -> component columnsStructural changes move an entity
Section titled “Structural changes move an entity”Writing Position.x changes data in place. Adding or removing a component
changes the archetype, so the entity moves to another table.
Position *position = ecs_get(entity, Position);position->x += 1.0f; /* same table */
ecs_add(entity, Velocity); /* Position -> Position + Velocity table */entity.get<Position>().x += 1.0f; // same tableentity.add<Velocity>(); // different tableMigration is a normal ECS operation, but it can invalidate pointers into the current batch. Keep it out of the hot loop where possible, or defer it.
Tags, resources and relations
Section titled “Tags, resources and relations”- A tag is a zero-sized component used as a fact:
Enemy,Selected, orDisabled. - A resource is one typed value in the world: time, input, configuration.
- A relation is an edge between entities:
ChildOf,GroupOf,Targets.
These are different tools. Choosing the correct one keeps tables compact and queries easy to read.
Continue
Section titled “Continue”Read Archetype storage for the storage consequences, then Components, Queries and Systems for the concrete API.