This guide explains how to use Coyote ECS from C code. Coyote ECS provides a C API that allows you to use the ECS functionality in C projects.
#include <coyote.h>
The C API provides the following basic types:
typedef uintptr_t world;
typedef uintptr_t entity;
typedef uintptr_t component;
typedef uintptr_t iterator;
typedef uintptr_t coyote_type;
world world = coyote_world_create();
if (world == 0) {
// Handle error
}
Components in C are defined as structs:
typedef struct position {
float x;
float y;
} position;
typedef struct velocity {
float x;
float y;
} velocity;
Use the COYOTE_MAKE_TYPE
macro to create component types:
static const coyote_type t_position = COYOTE_MAKE_TYPE(0, position);
static const coyote_type t_velocity = COYOTE_MAKE_TYPE(1, velocity);
// Create an entity
entity e = coyote_entity_create(world);
// Create components
component c_pos = coyote_component_create(world, t_position);
component c_vel = coyote_component_create(world, t_velocity);
// Attach components to entity
coyote_entity_attach(e, c_pos, t_position);
coyote_entity_attach(e, c_vel, t_velocity);
// Set component data
position* pos = coyote_component_get(c_pos);
pos->x = 0.0f;
pos->y = 0.0f;
velocity* vel = coyote_component_get(c_vel);
vel->x = 1.0f;
vel->y = 1.0f;
// Iterate over all components of a specific type
iterator it = coyote_components_iterator_filter(world, t_position);
component next;
while ((next = coyote_components_iterator_filter_next(it)) != 0) {
position* pos = coyote_component_get(next);
// Work with the position component
}
For parallel processing, you can iterate over components in chunks:
// Get the total number of components
int total_components = coyote_components_count(world);
int thread_count = 4; // Or use a thread pool
int chunk_size = (total_components + thread_count - 1) / thread_count;
// Process components in parallel
for (int i = 0; i < thread_count; i++) {
size_t start_idx = i * chunk_size;
size_t end_idx = (i + 1) * chunk_size;
if (end_idx > total_components) {
end_idx = total_components;
}
// Create an iterator for this chunk
iterator chunk_it = coyote_components_iterator_filter_range(world, t_position, start_idx, end_idx);
// Process the chunk
component next;
while ((next = coyote_components_iterator_filter_range_next(chunk_it)) != 0) {
position* pos = coyote_component_get(next);
// Work with the position component in this chunk
}
}
// Iterate over entities with specific components
iterator it = coyote_entities_iterator_filter(world, t_position);
entity next;
while ((next = coyote_entities_iterator_filter_next(it)) != 0) {
// Work with the entity
}
if (coyote_component_is(component, t_position)) {
// This is a position component
}
coyote_component_destroy(component);
Call the garbage collector when appropriate:
coyote_components_gc(world);
int entity_count = coyote_entities_count(world);
int component_count = coyote_components_count(world);
For high-performance applications, you can use the chunked iterator to process components in parallel:
#include <pthread.h>
#define NUM_THREADS 4
typedef struct {
world world;
coyote_type component_type;
size_t start_idx;
size_t end_idx;
} thread_data;
void* process_chunk(void* arg) {
thread_data* data = (thread_data*)arg;
iterator it = coyote_components_iterator_filter_range(
data->world,
data->component_type,
data->start_idx,
data->end_idx
);
component next;
while ((next = coyote_components_iterator_filter_range_next(it)) != 0) {
// Process the component
}
return NULL;
}
void process_components_parallel(world world, coyote_type component_type) {
int total_components = coyote_components_count(world);
int chunk_size = (total_components + NUM_THREADS - 1) / NUM_THREADS;
pthread_t threads[NUM_THREADS];
thread_data thread_args[NUM_THREADS];
// Create threads
for (int i = 0; i < NUM_THREADS; i++) {
size_t start_idx = i * chunk_size;
size_t end_idx = (i + 1) * chunk_size;
if (end_idx > total_components) {
end_idx = total_components;
}
thread_args[i].world = world;
thread_args[i].component_type = component_type;
thread_args[i].start_idx = start_idx;
thread_args[i].end_idx = end_idx;
pthread_create(&threads[i], NULL, process_chunk, &thread_args[i]);
}
// Wait for all threads to complete
for (int i = 0; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
}
}