API Reference
This page is an API map, not a second tutorial. Read the Quickstart and the concept manuals for explanations; use this page to find the public operation, its C spelling, and its C++ counterpart.
All examples use the public distribution header. The runtime is C17; the typed wrapper is C++20.
#include <siecs.h>#include <siecs.h>World lifecycle
Section titled “World lifecycle”There is one active world per process. Initialize it before registering types or creating handles, and finalize it after systems and tools stop using them.
ecs_init();ecs_progress();ecs_run_phase(EcsOnUpdate);ecs_quit();ecs_fini();ecs::init();ecs::progress();ecs::run_phase(EcsOnUpdate);ecs::quit();ecs::fini();| Concern | C API | C++ API |
|---|---|---|
| Optional frame cap | ecs_init_w_features() |
ecs::init({ .target_fps = 60 }) |
| Run all enabled systems | ecs_progress() |
ecs::progress() |
| Run one phase | ecs_run_phase() |
ecs::run_phase() |
| Stop a loop | ecs_quit() |
ecs::quit() |
The world owns entities, component registries, resources, queries, systems, observers and modules. Handles are non-owning and must not outlive the world.
Entities and components
Section titled “Entities and components”Entities are identities; components are typed data attached to them. The common operations are:
ecs_entity_t player = ecs_new();ecs_set(player, Position, { .x = 1.0f, .y = 2.0f });
if (ecs_has(player, Position)) { Position *position = ecs_get(player, Position); position->x += 1.0f;}
ecs_remove(player, Position);ecs_kill(player);auto player = ecs::entity::create() .set(Position{ .x = 1.0f, .y = 2.0f });
if (player.has<Position>()) { auto &position = player.get<Position>(); position.x += 1.0f;}
player.remove<Position>();player.kill();| Operation | C | C++ |
|---|---|---|
| Create | ecs_new(), ecs_new_no_reuse() |
entity::create(), create_no_reuse() |
| Test liveness | ecs_is_alive() |
entity::is_alive() |
| Add/remove | ecs_add(), ecs_remove() |
entity::add<T>(), remove<T>() |
| Test presence | ecs_has() |
entity::has<T>() |
| Read, required | ecs_get() |
entity::get<T>() |
| Read, nullable | ecs_try_get() |
entity::try_get<T>() |
| Write | ecs_set() |
entity::set() |
| Destroy | ecs_kill() |
entity::kill() |
| Disable | ecs_add(entity, Disabled) |
entity::disable() |
Register typed declarations once per world with ECS_COMPONENT_REGISTER() in C;
native C++ types register on first typed use. Generic code can use the _cid
functions (ecs_get_cid, ecs_set_cid, ecs_add_cid, ecs_remove_cid) and
ecs::component<T>().
Queries and iteration
Section titled “Queries and iteration”Queries match archetype tables and expose each matching table as a batch. Keep a
query id or query_handle for repeated work.
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 *positions = ecs_field(&it, 0); const Velocity *velocities = ecs_field(&it, 1); for (uint32_t i = 0; i < it.count; i++) { positions[i].x += velocities[i].x; }}ecs_query_fini(moving);auto moving = ecs::query() .require<Position>() .require<Velocity>() .build_handle();
moving.each([](Position &position, const Velocity &velocity) { position.x += velocity.x;});| Match or access | C | C++ |
|---|---|---|
| Required read | ecs_in(T) |
require<T>() |
| Required write | ecs_out(T), ecs_inout(T) |
non-const callback parameter |
| Optional read/write | ecs_in_optional(T), ecs_inout_optional(T) |
optional<T>() |
| Include/exclude without a field | ecs_filter(T), ecs_not(T) |
require<T>(), exclude<T>() |
| Relation presence | ecs_rel(R), ecs_rel_opt(R) |
with_relation<R>() |
| Exact target | ecs_to(R, target) |
to<R>(target) |
| Relation depth | ecs_depth(R, depth) |
depth<R>(depth) |
| Inherited read | ecs_up(T, R) |
up<T, R>() |
Fields are numbered by declaration order; filters and relation terms do not
consume a component field index. ecs_field_kind() distinguishes owned,
shared, and absent optional fields. Field pointers are borrowed and must not be
kept across a structural mutation.
Use ecs_query_each() for one-off C scans. A C query id is released by
ecs_query_fini(); a C++ query_handle releases its id automatically.
Systems and scheduling
Section titled “Systems and scheduling”A system is a persistent query callback attached to a phase. ecs_progress()
runs enabled systems in phase order; .after or .after() orders systems in
the same phase.
static void move_system(ecs_iter_t *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_system({ .name = "Move", .phase = EcsOnUpdate, .query = { .components = { ecs_inout(Position), ecs_in(Velocity) } }, .callback = move_system,});auto move = ecs::system("Move") .phase(EcsOnUpdate) .each([](Position &position, const Velocity &velocity) { position.x += velocity.x; });| Operation | C | C++ |
|---|---|---|
| Run all phases | ecs_progress() |
ecs::progress() |
| Run one phase | ecs_run_phase() |
ecs::run_phase() |
| Run one system | ecs_run_system() |
ecs::run_system() |
| Enable/disable | ecs_system_enable(), ecs_system_disable() |
ecs::enable_system(), disable_system() |
| Phase | ecs_phase_t |
EcsOnUpdate, EcsPostUpdate, etc. |
Structural mutations during iteration may migrate an entity and invalidate the current batch pointers. Use deferred mutation when the operation must happen after the current command scope.
Resources
Section titled “Resources”Resources are one typed value per world. They are not entity components and do not become query fields.
ecs_set_resource(Time, { .dt = 1.0f / 60.0f });const Time *time = ecs_get_resource_read(Time);ecs::set_resource(Time{ .dt = 1.0f / 60.0f });const Time &time = ecs::resource<const Time>();Use ecs_try_get_resource() or ecs::try_resource<T>() when absence is valid.
The id-based C functions use the separate ecs_resource_t registry:
ecs_resource_init, ecs_resource_find, ecs_resource_rid,
ecs_try_resource_rid, ecs_has_resource_rid, and ecs_remove_resource_rid.
Relations and inheritance
Section titled “Relations and inheritance”Relations connect a source entity to a target. ChildOf provides hierarchy;
IsA provides inheritance from an abstract base.
ecs_relate(child, ChildOf, parent);ecs_entity_t current_parent = ecs_target(child, ChildOf);
ecs_add(base, Abstract);ecs_is_a(instance, base);child.child_of(parent);auto current_parent = child.target<ecs::ChildOf>();
base.abstract();instance.is_a(base);Register custom relations with ECS_RELATION_DECLARE/DEFINE/REGISTER or
ecs::relation<T>(). Storage mode controls which query terms are valid:
ecs_to for ByTarget, ecs_depth for ByDepth, and ecs_rel for presence.
Observers and events
Section titled “Observers and events”Observers react to EcsOnAdd, EcsOnRemove, EcsOnSet, relation transitions,
or a custom event. Their payload pointers are borrowed for the callback.
static void on_position_set(ecs_observer_event_t *event) { const Position *position = event->trigger_data; log_position(event->entity, position);}
ecs_observer({ .on = EcsOnSet, .query = { .components = { ecs_in(Position) } }, .callback = on_position_set,});ecs::observe<ecs::OnSet>().each([](const Position &position) { log_position(position);});Use ecs_observer_trigger() or ecs::trigger<T>() for custom events. Relation
events carry old_target and new_target in ecs_relation_event_t.
Modules
Section titled “Modules”Modules group registrations and can enable or disable their captured systems and observers. Imports are idempotent in the active world; the first properties value wins.
ECS_MODULE_DECLARE(physics, { float gravity; });ECS_MODULE_DEFINE(physics);
void physics_import(const physics_props_t *props) { (void)props; ecs_system({ .name = "Move", .callback = move_system });}
ecs_module_id_t Physics = ECS_MODULE_IMPORT(physics, { .gravity = 9.81f });ecs_module_disable(Physics);struct Physics { float gravity;
static void import() { ecs::system("Move").each([](Position &position) { position.x += 1.0f; }); }};
auto physics = ecs::import<Physics>(9.81f);physics.disable();Public type families
Section titled “Public type families”| Family | Public C types | Public C++ types |
|---|---|---|
| Entity | ecs_entity_t |
ecs::entity |
| Component | ecs_component_t, ecs_component_desc_t |
ecs::component<T>, ecs::component_hooks<T> |
| Resource | ecs_resource_t, ecs_resource_desc_t |
ecs::resource_handle<T>, ecs::res<T> |
| Query | ecs_query_id_t, ecs_query_desc_t, ecs_iter_t |
ecs::query, ecs::query_handle |
| System | ecs_system_id_t, ecs_system_desc_t, ecs_phase_t |
ecs::system |
| Observer | ecs_observer_id_t, ecs_observer_desc_t, ecs_observer_event_t |
ecs::observer<T>, ecs::observer_event |
| Module | ecs_module_id_t, ecs_module_desc_t |
ecs::module_ref<T> |
| Relation | ecs_relation_id_t, ecs_relation_desc_t |
ecs::relation<T> |
For lifecycle preconditions, ownership rules, field semantics, deletion policy, and version guarantees, see API stability and the relevant manual. The public headers remain the final authority for overloads and exact descriptor layout.