Relations
Relations connect a source entity to one target entity. They use a separate
relation id space and are changed with ecs_relate() and ecs_unrelate(), not
with component ecs_set().
SIECS registers the built-in ChildOf relation during world initialization. It
is acyclic, uses ByDepth storage, and deleting a parent deletes its sources.
ecs_relate(child, ChildOf, parent);ecs_entity_t parent = ecs_target(child, ChildOf);ecs_unrelate(child, ChildOf);child.child_of(parent);auto current_parent = child.target<ChildOf>();child.unrelate<ChildOf>();Register a custom relation
Section titled “Register a custom relation”ECS_RELATION_DECLARE(GroupOf);ECS_RELATION_DEFINE(GroupOf, { .storage = EcsRelationByTarget, .on_delete_target = EcsRemoveRelation,});
ecs_init();ECS_RELATION_REGISTER(GroupOf);
ecs_relate(member, GroupOf, group);struct GroupOf {};
ecs::relation<GroupOf>({ .storage = EcsRelationByTarget, .on_delete_target = EcsRemoveRelation,});
auto member = ecs::entity::create().relate<GroupOf>(group);For runtime-created relations, use ecs_relation_init() with an
ecs_relation_desc_t. A declared relation must be registered once per world
with ECS_RELATION_REGISTER().
Storage modes
Section titled “Storage modes”Choose the storage mode according to how the relation is queried:
| Storage | Use | Query support |
|---|---|---|
EcsRelationDense |
The target is stored in a dense per-source column. | ecs_rel; ecs_targets() returns contiguous target records. |
EcsRelationByDepth |
Acyclic hierarchies where depth and depth order matter. | ecs_rel, ecs_depth, ecs_targets(), and ecs_order_by_depth(). |
EcsRelationByTarget |
Many sources share a target and exact target queries or target ordering are common. | ecs_rel, ecs_to(), ecs_up(), and ecs_order_by_target(). |
EcsRelationByDepth requires .acyclic = true. ecs_to() requires
ByTarget; ecs_depth() and ecs_order_by_depth() require ByDepth.
The relation target is always an entity handle. It must be live when passed to
ecs_relate(). Retargeting replaces the existing target for that relation;
calling ecs_relate() with the current target is a no-op.
Inspect a relation
Section titled “Inspect a relation”if (ecs_has_relation(source, GroupOf)) { ecs_entity_t target = ecs_target(source, GroupOf); /* target includes its generation. */}
if (ecs_has_relation_to(source, GroupOf, group)) { /* source points exactly to group. */}if (source.has_relation<GroupOf>()) { auto target = source.target<GroupOf>();}
if (source.target<GroupOf>().id() == group.id()) { /* source points exactly to group. */}Use the id-based variants when the relation id is dynamic:
ecs_has_relation_id(), ecs_has_relation_to_id(), ecs_target_id(),
ecs_relate_id(), and ecs_unrelate_id().
Query relations
Section titled “Query relations”Relation terms are filters and do not create component fields:
ecs_query_id_t members = ecs_query({ .relations = { ecs_to(GroupOf, group) },});auto members = ecs::query().to<GroupOf>(group).build_handle();The available terms are ecs_rel() for presence, ecs_rel_opt() for an
optional relation, ecs_not_rel() for absence, ecs_to() for an exact
ByTarget target, and ecs_depth() for a ByDepth depth. Use
ecs_targets() for contiguous Dense or ByDepth target records, and
ecs_target_shared() for the target shared by a ByTarget batch.
For C++, use .with_relation<Relation>(), .to<Relation>(target),
.depth<Relation>(depth), .order_by_depth<Relation>(), and
.order_by_target<Relation>() on ecs::query.
Target deletion
Section titled “Target deletion”The on_delete_target policy controls what happens when a target is killed:
| Policy | Effect |
|---|---|
EcsRemoveRelation |
Remove the relation from every source. Sources remain alive. |
EcsDeleteSources |
Kill every source that points to the target. |
Removing or retargeting a relation also updates the runtime bookkeeping used by
the relation storage. Relation transitions caused by target deletion emit the
same relation observer events as explicit ecs_unrelate() calls.
Deferred relation changes
Section titled “Deferred relation changes”Relation changes can be deferred while a system or an explicit defer scope is active:
ecs_defer_begin();ecs_relate(source, GroupOf, first_group);ecs_relate(source, GroupOf, final_group);ecs_defer_end();{ ecs_defer_begin(); source.relate<GroupOf>(first_group); source.relate<GroupOf>(final_group); ecs_defer_end();}Multiple deferred changes to the same relation are coalesced to the final target before the command buffer is flushed.