Skip to content

Archetype Storage

An archetype entity component system groups entities by their exact set of components. Every distinct component set identifies a table, and every component stored by that table has a contiguous column.

SIECS uses this model to turn an ECS query into iteration over matching tables instead of repeated lookups across unrelated component stores.

Consider three entities:

EntityComponentsArchetype
PlayerPosition, Velocity, HealthMoving actor
EnemyPosition, Velocity, HealthMoving actor
TreePosition, HealthStatic actor

The player and enemy share one table because they have the same component set. The tree belongs to another table because it does not have Velocity.

An entity handle identifies a row in one of these tables. The handle is stable while its storage location may change.

Conceptually, the moving-actor table is laid out as separate arrays:

entities: [player, enemy, ...]
Position: [p0, p1, ...]
Velocity: [v0, v1, ...]
Health: [h0, h1, ...]

A query for Position and Velocity can process those columns sequentially. It does not need to visit unrelated entities or perform one component lookup for every row.

Tags are zero-sized components. They participate in the archetype identity and query matching without adding a data column.

A persistent SIECS query records the table identifiers that satisfy its terms. Iteration then returns one matching table batch at a time:

ecs_query_id_t moving = ecs_query({
    .terms = {
        ecs_inout(Position),
        ecs_in(Velocity),
        ecs_filter(Visible),
    },
});

Position and Velocity produce fields. Visible only constrains matching, so it does not consume a field index.

When a compatible table is created later, the query index can associate that table with the persistent query. This keeps repeated iteration focused on matching storage.

Changing an existing component value writes into the current column. Changing the component set is different:

  1. SIECS finds or creates the destination archetype table.
  2. Shared component values move to the destination row.
  3. Added component storage is initialized.
  4. Removed component storage is discarded according to its lifecycle.
  5. The entity handle is updated to its new row.

Operations such as ecs_add(), ecs_remove(), and setting a previously absent component are structural changes. They are valid operations, but they should not dominate a hot iteration loop.

Both models can implement a capable ECS, but they optimize different work:

ConcernArchetype storageSparse-set storage
GroupingExact component setsUsually one store per component
Query iterationMatching tables and contiguous columnsDrive one component store and test membership in others
Adding/removing componentsMay migrate a row between tablesUsually updates independent component stores
Stable component setsStrong fitAlso supported
Frequent structural churnMore migration workOften less coupled
Batch processingNatural table batchesDepends on store alignment and query strategy

This is a design tradeoff, not a universal ranking. Archetypes are a strong fit when many entities keep stable component sets and systems repeatedly process large matching batches. A sparse-set design can be attractive when independent component insertion and removal dominates the workload.

  • Keep persistent queries for repeated work.
  • Resolve query fields once per batch.
  • Update existing component values in place.
  • Apply structural changes outside tight loops when practical.
  • Use filter terms for components that affect matching but are not read.
  • Model stable state with tags without expecting tag toggles to be free.

Continue with ECS theory for the user-facing model, queries for query terms, and the performance guide for hot-path rules.