C ECS example

A complete C ECS example.

Register data, create an entity, run a system, and compile the result with the standalone SIECS distribution.

Copy the program

Place siecs.h and siecs.c beside main.c, then compile the program below.

#include <siecs.h>

ECS_COMPONENT(Position, {
    float x;
    float y;
});

ECS_COMPONENT(Velocity, {
    float x;
    float y;
});

static void Move(ecs_iter_t *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;
        positions[i].y += velocities[i].y;
    }
}

int main(void) {
    ecs_init();

    ECS_COMPONENT_REGISTER(Position);
    ECS_COMPONENT_REGISTER(Velocity);

    ecs_system({
        .query.terms = {
            ecs_inout(Position),
            ecs_in(Velocity),
        },
        .callback = Move,
        .phase = EcsOnUpdate,
    });

    ecs_entity_t entity = ecs_new();
    ecs_set(entity, Position, { 0, 0 });
    ecs_set(entity, Velocity, { 1, 1 });

    ecs_progress();
    ecs_fini();
    return 0;
}

Declare the data

ECS_COMPONENT declares a typed data component. ECS_COMPONENT_REGISTER creates its runtime identifier in the active world.

Position and velocity stay independent, so systems can request each value with a clear access mode.

Process matching batches

The system query requests writable Position data and read-only Velocity data.

Each callback receives a matching batch. Resolve each field once, then let the inner loop update aligned rows.

Create the entity

ecs_new() creates the handle. The two ecs_set() calls attach the values used by the movement system.

ecs_progress() runs the enabled system in phase order.

Compile and run

cc -std=c23 -I. main.c siecs.c -pthread -o ecs_example

The command builds the application and SIECS runtime together. Continue with the C API guide when you are ready for resources, observers, relations, and modules.