Resources
Resources are typed values stored once per world. Use them for state that is unique to the world, not attached to each entity: frame time, input state, renderer handles, asset registries, shared config, or global simulation settings.
Resources have their own ecs_resource_t ids. They do not consume component
ids, they are not added to an entity, they are not query terms, and they do not
consume an ecs_field() slot.
SIECS provides the builtin DeltaTime resource. It is always registered and
contains the current frame delta in seconds:
const DeltaTime *delta_time = ecs_get_resource_read(DeltaTime);float seconds = delta_time->value;const auto &delta_time = ecs::resource<const DeltaTime>();float seconds = delta_time.value;Declare a Resource
Section titled “Declare a Resource”Resources are declared once and stored per world:
ECS_RESOURCE_DECLARE(Time, { float dt; float elapsed;});
ECS_RESOURCE_DEFINE(Time);ECS_RESOURCE_REGISTER(Time);#include "resources.h" // contains ECS_RESOURCE_DECLARE(Time, ...)
auto time = ecs::resource_handle<Time>();time.set(Time{ .dt = 0.016f });The resource macros intentionally mirror component macros, but resources are registered in a dedicated resource registry. When a resource is declared in C, the C++ helpers reuse its C id, descriptor, lifecycle operations, and hooks.
Use ECS_RESOURCE_DECLARE_CPP when the shared resource also needs C++-only
methods:
ECS_RESOURCE_DECLARE(Time, { float dt;});ECS_RESOURCE_DECLARE_CPP(Time, ECS_CPP_FIELDS( float dt; ), ECS_CPP_METHODS( bool valid() const { return dt > 0.0f; } ));The declaration remains valid in C, where the method block is ignored. C++ methods must not add instance data, so the C and C++ resource layouts stay identical.
Set and Read
Section titled “Set and Read”ecs_set_resource() creates or replaces the resource value:
ecs_set_resource(Time, { .dt = 0.016f, .elapsed = 0.0f,});ecs::set_resource(Time{ .dt = 0.016f, .elapsed = 0.0f });Use ecs_get_resource() when the resource must exist:
Time *time = ecs_get_resource(Time);time->elapsed += time->dt;auto &time = ecs::resource<Time>();time.elapsed += time.dt;Use ecs_get_resource_read() when the caller only needs read access:
const Time *time = ecs_get_resource_read(Time);const auto &time = ecs::resource<const Time>();If absence is valid, use the nullable helpers:
Time *time = ecs_try_get_resource(Time);if (time != NULL) { time->elapsed += time->dt;}
const Time *read_time = ecs_try_get_resource_read(Time);if (auto *time = ecs::try_resource<Time>()) { time->elapsed += time->dt;}
const auto *read_time = ecs::try_resource<const Time>();ecs_get_resource() and ecs_get_resource_read() assert when the resource does not
exist. The try variants return NULL.
Presence and Removal
Section titled “Presence and Removal”Check whether a resource exists with ecs_has_resource():
if (!ecs_has_resource(Time)) { ecs_set_resource(Time, { .dt = 0.0f, .elapsed = 0.0f, });}if (!ecs::has_resource<Time>()) { ecs::set_resource(Time{ .dt = 0.0f, .elapsed = 0.0f });}Remove it with ecs_remove_resource():
ecs_remove_resource(Time);ecs::remove_resource<Time>();Removing a missing resource is a no-op.
Use Resources in C Systems
Section titled “Use Resources in C Systems”In C, systems receive an ecs_iter_t *. Read resources from the active world
before the entity loop:
static void move_system(ecs_iter_t *it) { const Time *time = ecs_get_resource_read(Time); 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 * time->dt; positions[i].y += velocities[i].y * time->dt; }}
ecs_system({ .name = "Move", .phase = EcsOnUpdate, .query = { .components = { ecs_inout(Position), ecs_in(Velocity) }, }, .callback = move_system,});ecs::system("Move").each([]( ecs::res<const Time> time, Position &position, const Velocity &velocity) { position.x += velocity.x * time->dt; position.y += velocity.y * time->dt;});The resource is declared in query.resources as access metadata. It is never a
component, field, or archetype criterion. The system above matches entities with
Position and Velocity; it does not require entities to have Time.
ID-based API
Section titled “ID-based API”Typed helpers are wrappers around the id-based API:
void ecs_set_resource_rid(ecs_resource_t id, const void *data);void *ecs_resource_rid(ecs_resource_t id);void *ecs_try_resource_rid(ecs_resource_t id);bool ecs_has_resource_rid(const ecs_resource_t id);void ecs_remove_resource_rid(ecs_resource_t id);Time time_value{ .dt = 0.016f };auto time = ecs::resource_handle<Time>();time.set(time_value);time.get().dt += 0.001f;This is useful for generic module code:
ecs_resource_t time_id = ecs_id(Time);
if (ecs_has_resource_rid(time_id)) { const Time *time = ecs_resource_rid(time_id); /* use time */}#include <cstdio>
auto time = ecs::resource_handle<const Time>();
if (time.has()) { if (time.get().dt >= 0.0f) { std::printf("valid time\n"); }}Resources have dedicated resource hooks:
on_setruns whenecs_set_resource()writes a value.on_removeruns whenecs_remove_resource()removes the value.on_removealso runs duringecs_fini()for resources still present in the world.
Hooks receive only the resource value pointer. The callback does not receive a world argument.
static void on_time_set(const void *data) { const Time *time = data; if (time->dt < 0.0f) { /* reject or record invalid data */ }}#include <cstdlib>
static void on_time_set(const Time &time) { if (time.dt < 0.0f) { std::abort(); }}
ecs::resource_hooks<Time> hooks{ .on_set = on_time_set };auto time = ecs::resource_handle<Time>(hooks);time.set(Time{ .dt = 0.016f });Performance notes
Section titled “Performance notes”Resource lookup is a direct index by ecs_resource_t, so access is O(1). The
value is stored once in the world and is independent from table iteration.
For hot C systems, resolve the resource once before the loop over it->count,
then reuse the pointer for every entity in the batch.
In typed C++, ecs::res<const T> declares EcsIn and ecs::res<T> declares
EcsInOut in query.resources. Neither form creates a component term.