|
| typedef struct ecs_s | ecs_t |
| | ECS context.
|
| |
| typedef ECS_ID_TYPE | ecs_id_t |
| | ID used for entity and components.
|
| |
| typedef ECS_MASK_TYPE | ecs_mask_t |
| | Type for value used in system matching.
|
| |
| typedef int32_t | ecs_ret_t |
| | Return code for system callback and calling functions.
|
| |
| typedef struct ecs_entity_t | ecs_entity_t |
| | An entity handle.
|
| |
| typedef struct ecs_comp_t | ecs_comp_t |
| | A component handle.
|
| |
| typedef struct ecs_system_t | ecs_system_t |
| | A system handle.
|
| |
| typedef void(* | ecs_on_add_fn) (ecs_t *ecs, ecs_entity_t entity, ecs_comp_t comp, const void *args, void *udata) |
| | Called when a component is created (via ecs_add)
|
| |
| typedef void(* | ecs_on_remove_fn) (ecs_t *ecs, ecs_entity_t entity, ecs_comp_t comp, void *udata) |
| | Called when a component is destroyed (via ecs_remove or ecs_destroy)
|
| |
| typedef void(* | ecs_on_set_fn) (ecs_t *ecs, ecs_entity_t entity, ecs_comp_t comp, void *udata) |
| | Called when a component's data is set (via ecs_set)
|
| |
| typedef ecs_ret_t(* | ecs_system_fn) (ecs_t *ecs, ecs_entity_t *entities, size_t entity_count, void *udata) |
| | System callback.
|
| |
| typedef void(* | ecs_on_join_fn) (ecs_t *ecs, ecs_entity_t entity, void *udata) |
| | Called when an entity is added to a system.
|
| |
| typedef void(* | ecs_on_leave_fn) (ecs_t *ecs, ecs_entity_t entity, void *udata) |
| | Called when an entity is removed from a system.
|
| |
|
| ecs_t * | ecs_new (size_t entity_capacity, void *mem_ctx) |
| | Creates an ECS context.
|
| |
| void | ecs_free (ecs_t *ecs) |
| | Destroys an ECS context.
|
| |
| void | ecs_reset (ecs_t *ecs) |
| | Removes all entities from the ECS, preserving systems and components.
|
| |
| ecs_comp_t | ecs_define_component (ecs_t *ecs, size_t size, const ecs_comp_desc_t *desc) |
| | Defines a component.
|
| |
| ecs_system_t | ecs_define_system (ecs_t *ecs, ecs_system_fn system_cb, const ecs_sys_desc_t *desc) |
| | Defines a system.
|
| |
| void | ecs_require (ecs_t *ecs, ecs_system_t sys, ecs_comp_t comp) |
| | Entities are processed by the target system if they have all of the the components required by the system.
|
| |
| void | ecs_exclude (ecs_t *ecs, ecs_system_t sys, ecs_comp_t comp) |
| | Excludes entities having the specified component from being added to the target system.
|
| |
| void | ecs_enable_system (ecs_t *ecs, ecs_system_t sys) |
| | Enables a system.
|
| |
| void | ecs_disable_system (ecs_t *ecs, ecs_system_t sys) |
| | Disables a system.
|
| |
| void | ecs_set_system_callbacks (ecs_t *ecs, ecs_system_t sys, ecs_system_fn system_cb, ecs_on_join_fn on_join, ecs_on_leave_fn on_leave) |
| | Updates the callbacks for an existing system.
|
| |
| void | ecs_set_system_udata (ecs_t *ecs, ecs_system_t sys, void *udata) |
| | Sets the user data for a system.
|
| |
| void * | ecs_get_system_udata (ecs_t *ecs, ecs_system_t sys) |
| | Gets the user data from a system.
|
| |
| void | ecs_set_system_mask (ecs_t *ecs, ecs_system_t sys, ecs_mask_t mask) |
| | Sets the system's mask.
|
| |
| ecs_mask_t | ecs_get_system_mask (ecs_t *ecs, ecs_system_t sys) |
| | Returns the system mask.
|
| |
| ecs_entity_t * | ecs_get_entity_array (ecs_t *ecs, ecs_system_t sys) |
| | Returns the entities associated with the specified system.
|
| |
| size_t | ecs_get_entity_count (ecs_t *ecs, ecs_system_t sys) |
| | Returns the number of entities assigned to the specified system.
|
| |
| ecs_entity_t | ecs_create (ecs_t *ecs) |
| | Creates an entity.
|
| |
| bool | ecs_is_ready (ecs_t *ecs, ecs_entity_t entity) |
| | Returns true if the entity is currently active and has not been queued for destruction.
|
| |
| bool | ecs_has (ecs_t *ecs, ecs_entity_t entity, ecs_comp_t comp) |
| | Test if entity has the specified component.
|
| |
| void | ecs_add (ecs_t *ecs, ecs_entity_t entity, ecs_comp_t comp, void *args) |
| | Adds a component instance to an entity.
|
| |
| void * | ecs_get (ecs_t *ecs, ecs_entity_t entity, ecs_comp_t comp) |
| | Gets a component instance associated with an entity.
|
| |
| void | ecs_set (ecs_t *ecs, ecs_entity_t entity, ecs_comp_t comp, void *data) |
| | Copies data into a component instance associated with an entity.
|
| |
| void | ecs_destroy (ecs_t *ecs, ecs_entity_t entity) |
| | Destroys an entity.
|
| |
| void | ecs_remove (ecs_t *ecs, ecs_entity_t entity, ecs_comp_t comp) |
| | Removes a component instance from an entity.
|
| |
| ecs_ret_t | ecs_run_system (ecs_t *ecs, ecs_system_t sys, ecs_mask_t mask) |
| | Update an individual system.
|
| |
| ecs_ret_t | ecs_run_systems (ecs_t *ecs, ecs_mask_t mask) |
| | Updates all systems.
|
| |
A pure and simple ECS.
Licensing information at end of header
Features:
- Written in C11
- Single header library for easy build system integration
- Excellent performance
- Pure ECS design (strict separation between data and logic)
- Simple and concise API
- Permissive license (zlib or public domain)
Summary:
This library implements an ECS (Entity-Component-System). Entities (sometimes called game objects) are defined by their components. For example, an entity might have position, sprite, and physics components. Systems operate on the components of entities that match the system's requirements. Entities are matched to systems based upon which components they have and also the system's matching crieria.
In the above example, a sprite renderer system would match entities having poition and sprite components. The system would send the appropriate geometry and texture ID to the game's graphics API.
Traditional game engines tightly couple state with logic. For example, in C++ a game object typically has its own update method that operates on that state. They also usually rely heavily on inheritance.
If the state specified by the class changes this could ripple through the class, and perhaps subclasses as well. It could well be that the class no longer belongs in the existing class hierarchy forcing even more revisions. It could even be true that a class doesn't neatly fit into the inheritance tree at all.
An ECS solves these problems while also granting more flexibility in general. In an ECS there is a clear separation between data (components) and logic (systems), which makes it possible to build complex simulations with fewer assumptions about how that data will be used. In an ECS it is effortless to change functionality, by either adding or removing components from entities, and/or by changing system requirements. Adding new logic is also simple as well, just defining a new system!
Please see the examples and unit tests for more details.
Masks:
Masks are a new feature in 3.0. Systems to are assigned to categories (using a bitmask) at definition and then can selectively invoke those systems at runtime (also using a bitmask).
Note that passing 0 into ecs_define_system means the system matches all categories.
If ecs_system_t sys = ecs_define_system(ecs, (1 << 0) | (1 << 1), ...) Then,
This will run sys:
ecs_run_system(ecs, sys, (1 << 0) | (1 << 1));
And so will this,
ecs_run_system(ecs, sys, (1 << 1));
But this will not,
ecs_run_system(ecs, sys, (1 << 3));
Nor will this:
ecs_run_system(ecs, sys, 0);
Revision History:
- 3.0 (2025/10/22):
- Typesafe entity, component, and system handles
- System category masks
- More descriptive names for some functions in the public API
- Invalid entity ID is now 0
- The 'dt' parameter has been removed
- Optimizations
- Some function name changes
- Significant internal refactoring
- 3.1 (2026/02/01):
- Fixed sparse set related bugs
- More sophisticated logic regarding adding entities to/from systems
- Functions ecs_queue_remove and ecs_queue_destroy have been removed. (they can be replaced directly by ecs_remove and ecs_destroy respectively)
- Improved unit test quality and coverage
- 3.2 (2026/03/09):
- Capacity overflow detection
- Invalid entity ID value has been reverted to max ID value
- 3.3 (2026/06/05):
- New command queue that ensures add/remove/set/destroy operations are performed in the order the corresponding functions were called.
- Pointers obtained by ecs_get are now stable.
- The ecs_add constructor/destructor callbacks have been replaced by on_add/on_remove callbacks.
- A new function 'ecs_set' has been added. If the entity does not have the component it is added first, then the component's value is set. If called during system iteration, then setting the value is deferred until after the system completes. This function effectively replaces the old ecs_add/constructor functionality.
- ecs_require_component and ecs_exclude_component have been renamed to ecs_require and ecs_exclude.
- Renamed ecs_get_system_entity_count to ecs_get_entity_count.
- Added ecs_get_entity_array that return the entities associated with a system.
- 3.4 (2026/06/18):
- ecs_add now accepts an optional 'args' pointer that is forwarded to the on_add callback. When the component is defined with a non-zero args_size, the args are copied into the command arena so the caller need not keep them alive (relevant for deferred adds during system iteration).
- Components may now specify a default_value that is copied into the component on add.
Usage:
To use this library in your project, add the following
#define PICO_ECS_IMPLEMENTATION #include "pico_ecs.h"
to a source file (once), then simply include the header normally.
Macros:
- ECS_MALLOC(size, ctx) (default: malloc)
- ECS_REALLOC(ptr, size, ctx) (default: realloc)
- ECS_FREE(ptr, ctx) (default: free)
- ECS_MEMSET (default: memset)
- ECS_MEMCPY (default: memcpy)
The ctx parameter is sometimes used by custom allocators
Constants:
- PICO_ECS_MAX_COMPONENTS (default: 32)
- PICO_ECS_MAX_SYSTEMS (default: 16)
- PICO_ECS_COMP_BLOCK_SIZE (default: 64)
Must be defined before PICO_ECS_IMPLEMENTATION