Pico Headers
Loading...
Searching...
No Matches
Classes | Macros | Typedefs | Functions
pico_ecs.h File Reference

A pure and simple ECS written in C99. More...

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
Include dependency graph for pico_ecs.h:

Go to the source code of this file.

Classes

struct  ecs_entity_t
 An entity handle. More...
 
struct  ecs_comp_t
 A component handle. More...
 
struct  ecs_system_t
 A system handle. More...
 

Macros

#define ECS_ID_TYPE   uint64_t
 Determine ID type.
 
#define ECS_MASK_TYPE   uint64_t
 Determine mask type.
 

Typedefs

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_constructor_fn) (ecs_t *ecs, ecs_entity_t entity, void *comp_ptr, void *args)
 Called when a component is created (via ecs_add)
 
typedef void(* ecs_destructor_fn) (ecs_t *ecs, ecs_entity_t entity, void *comp_ptr)
 Called when a component is destroyed (via ecs_remove or ecs_destroy)
 
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_added_fn) (ecs_t *ecs, ecs_entity_t entity, void *udata)
 Called when an entity is added to a system.
 
typedef void(* ecs_removed_fn) (ecs_t *ecs, ecs_entity_t entity, void *udata)
 Called when an entity is removed from a system.
 

Functions

bool ecs_is_invalid_entity (ecs_entity_t entity)
 Returns true if the entity is invalid and false otherwise.
 
ecs_entity_t ecs_invalid_entity ()
 Returns an invalid entity.
 
ecs_tecs_new (size_t entity_count, 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, ecs_constructor_fn constructor, ecs_destructor_fn destructor)
 Defines a component.
 
ecs_system_t ecs_define_system (ecs_t *ecs, ecs_mask_t mask, ecs_system_fn system_cb, ecs_added_fn add_cb, ecs_removed_fn remove_cb, void *udata)
 Defines a system.
 
void ecs_require_component (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_component (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_added_fn add_cb, ecs_removed_fn remove_cb)
 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.
 
size_t ecs_get_system_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_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.
 
void ecs_queue_destroy (ecs_t *ecs, ecs_entity_t entity)
 Queues an entity for destruction after the current system returns.
 
void ecs_queue_remove (ecs_t *ecs, ecs_entity_t entity, ecs_comp_t comp)
 Queues a component for removal from the specified 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.
 

Detailed Description

A pure and simple ECS written in C99.


Licensing information at end of header

Features:

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.

Version 2.4 to 3.0 Migration Guide:

Version 3.0 is a major departure from 2.4. Here is a short guide to help make the leap to 3.0.

  1. Make the following substitutions:
    • ecs_register_system -> ecs_define_system
    • ecs_register_component -> ecs_define_component
    • ecs_update_system -> ecs_run_system
    • ecs_update_systems -> ecs_run_systems
  2. Remove the 'dt' parameter from system callbacks, replacing it with a function call or global variable where necessary.
  3. Replace 'int entity_count' with 'size_t entity_count' in all system callbacks
  4. Insert a mask value of 0 into ecs_define_system calls, for example, ecs_define_system(ecs, 0, ...)
  5. Ensure all update calls have the form ecs_run_system(ctx, sys, 0) and/or ecs_run_systems(ctx, 0)
  6. Replace raw IDs with typesafe handles.

If you encounter any difficultlies with any of these steps and/or your project doesn't compile once you're finished, please submit an issue.

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:

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:

The ctx parameter is sometimes used by custom allocators

Constants:

Must be defined before PICO_ECS_IMPLEMENTATION

Macro Definition Documentation

◆ ECS_ID_TYPE

#define ECS_ID_TYPE   uint64_t

Determine ID type.

◆ ECS_MASK_TYPE

#define ECS_MASK_TYPE   uint64_t

Determine mask type.

Typedef Documentation

◆ ecs_t

typedef struct ecs_s ecs_t

ECS context.

◆ ecs_id_t

ID used for entity and components.

◆ ecs_mask_t

Type for value used in system matching.

◆ ecs_ret_t

typedef int32_t ecs_ret_t

Return code for system callback and calling functions.

◆ ecs_entity_t

typedef struct ecs_entity_t ecs_entity_t

An entity handle.

◆ ecs_comp_t

typedef struct ecs_comp_t ecs_comp_t

A component handle.

◆ ecs_system_t

typedef struct ecs_system_t ecs_system_t

A system handle.

◆ ecs_constructor_fn

typedef void(* ecs_constructor_fn) (ecs_t *ecs, ecs_entity_t entity, void *comp_ptr, void *args)

Called when a component is created (via ecs_add)

Parameters
ecsThe ECS context
entityThe entity being constructed
ptrThe pointer to the component

◆ ecs_destructor_fn

typedef void(* ecs_destructor_fn) (ecs_t *ecs, ecs_entity_t entity, void *comp_ptr)

Called when a component is destroyed (via ecs_remove or ecs_destroy)

Parameters
ecsThe ECS context
entityThe entity being destoryed
ptrThe pointer to the component

◆ ecs_system_fn

typedef ecs_ret_t(* ecs_system_fn) (ecs_t *ecs, ecs_entity_t *entities, size_t entity_count, void *udata)

System callback.

Systems implement the core logic of an ECS by manipulating entities and components.

Parameters
ecsThe ECS context
entitiesAn array of entities managed by the system
entity_countThe number of entities in the array
udataThe user data associated with the system

◆ ecs_added_fn

typedef void(* ecs_added_fn) (ecs_t *ecs, ecs_entity_t entity, void *udata)

Called when an entity is added to a system.

Parameters
ecsThe ECS context
entityThe entity being added
udataThe user data passed to the callback

◆ ecs_removed_fn

typedef void(* ecs_removed_fn) (ecs_t *ecs, ecs_entity_t entity, void *udata)

Called when an entity is removed from a system.

Parameters
ecsThe ECS context
entityThe enitty being removed
udataThe user data passed to the callback

Function Documentation

◆ ecs_is_invalid_entity()

bool ecs_is_invalid_entity ( ecs_entity_t  entity)

Returns true if the entity is invalid and false otherwise.

◆ ecs_invalid_entity()

ecs_entity_t ecs_invalid_entity ( )

Returns an invalid entity.

◆ ecs_new()

ecs_t * ecs_new ( size_t  entity_count,
void *  mem_ctx 
)

Creates an ECS context.

Parameters
entity_countThe inital number of entities to pre-allocated
mem_ctxA context for a custom allocator
Returns
An ECS context or NULL if out of memory

◆ ecs_free()

void ecs_free ( ecs_t ecs)

Destroys an ECS context.

Parameters
ecsThe ECS context

◆ ecs_reset()

void ecs_reset ( ecs_t ecs)

Removes all entities from the ECS, preserving systems and components.

◆ ecs_define_component()

ecs_comp_t ecs_define_component ( ecs_t ecs,
size_t  size,
ecs_constructor_fn  constructor,
ecs_destructor_fn  destructor 
)

Defines a component.

Defines a component with the specfied size in bytes. Components define the game state (usually contained within structs) and are manipulated by systems.

Parameters
ecsThe ECS context
sizeThe number of bytes to allocate for each component instance
constructorCalled when a component is created (disabled if NULL)
destructorCalled when a component is destroyed (disabled if NULL)
udataData passed to callbacks (can be NULL)
Returns
A component handle

◆ ecs_define_system()

ecs_system_t ecs_define_system ( ecs_t ecs,
ecs_mask_t  mask,
ecs_system_fn  system_cb,
ecs_added_fn  add_cb,
ecs_removed_fn  remove_cb,
void *  udata 
)

Defines a system.

Defines a system with the specified parameters. Systems contain the core logic of a game by manipulating game state as defined by components.

Parameters
ecsThe ECS context
maskBitmask that determines which categories the system belongs to. A value of 0 matches all categories
system_cbCallback that is fired every update
add_cbCalled when an entity is added to the system (can be NULL)
remove_cbCalled when an entity is removed from the system (can be NULL)
udataThe user data passed to the callbacks
Returns
A system handle

◆ ecs_require_component()

void ecs_require_component ( 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.

Parameters
ecsThe ECS context
sysThe target system
compA component to require

◆ ecs_exclude_component()

void ecs_exclude_component ( ecs_t ecs,
ecs_system_t  sys,
ecs_comp_t  comp 
)

Excludes entities having the specified component from being added to the target system.

Parameters
ecsThe ECS context
sysThe target system
compA component to exclude

◆ ecs_enable_system()

void ecs_enable_system ( ecs_t ecs,
ecs_system_t  sys 
)

Enables a system.

Parameters
ecsThe ECS context
sys_idThe specified system

◆ ecs_disable_system()

void ecs_disable_system ( ecs_t ecs,
ecs_system_t  sys 
)

Disables a system.

Parameters
ecsThe ECS context
sysThe specified system

◆ ecs_set_system_callbacks()

void ecs_set_system_callbacks ( ecs_t ecs,
ecs_system_t  sys,
ecs_system_fn  system_cb,
ecs_added_fn  add_cb,
ecs_removed_fn  remove_cb 
)

Updates the callbacks for an existing system.

Parameters
ecsThe ECS context
sysThe system
system_cbCallback that is fired every update
add_cbCalled when an entity is added to the system (can be NULL)
remove_cbCalled when an entity is removed from the system (can be NULL)

◆ ecs_set_system_udata()

void ecs_set_system_udata ( ecs_t ecs,
ecs_system_t  sys,
void *  udata 
)

Sets the user data for a system.

Parameters
ecsThe ECS context
sysThe system
udataThe user data to set

◆ ecs_get_system_udata()

void * ecs_get_system_udata ( ecs_t ecs,
ecs_system_t  sys 
)

Gets the user data from a system.

Parameters
ecsThe ECS context
sysThe system
Returns
The system's user data

◆ ecs_set_system_mask()

void ecs_set_system_mask ( ecs_t ecs,
ecs_system_t  sys,
ecs_mask_t  mask 
)

Sets the system's mask.

Parameters
ecsThe ECS context
sysThe system
maskThe mask to set

◆ ecs_get_system_mask()

ecs_mask_t ecs_get_system_mask ( ecs_t ecs,
ecs_system_t  sys 
)

Returns the system mask.

Parameters
ecsThe ECS context
sysThe system
Returns
The system's mask

◆ ecs_get_system_entity_count()

size_t ecs_get_system_entity_count ( ecs_t ecs,
ecs_system_t  sys 
)

Returns the number of entities assigned to the specified system.

◆ ecs_create()

ecs_entity_t ecs_create ( ecs_t ecs)

Creates an entity.

Parameters
ecsThe ECS context
Returns
The new entity

◆ ecs_is_ready()

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.

Parameters
ecsThe ECS context
entityThe target entity

◆ ecs_has()

bool ecs_has ( ecs_t ecs,
ecs_entity_t  entity,
ecs_comp_t  comp 
)

Test if entity has the specified component.

Parameters
ecsThe ECS context
entityThe entity
compThe component
Returns
True if the entity has the component

◆ ecs_add()

void * ecs_add ( ecs_t ecs,
ecs_entity_t  entity,
ecs_comp_t  comp,
void *  args 
)

Adds a component instance to an entity.

Parameters
ecsThe ECS context
entityThe entity
compThe component
Returns
The component data

◆ ecs_get()

void * ecs_get ( ecs_t ecs,
ecs_entity_t  entity,
ecs_comp_t  comp 
)

Gets a component instance associated with an entity.

Parameters
ecsThe ECS context
entityThe entity
compThe component
Returns
The component data

◆ ecs_destroy()

void ecs_destroy ( ecs_t ecs,
ecs_entity_t  entity 
)

Destroys an entity.

Destroys an entity, releasing resources and returning it to the pool.

WARNING: This function may change the order of a system's entity array. It should be used with caution. A better option in most circumstances is to use the ecs_queue_destroy function, which destroys the entity after the system has finished executing.

Parameters
ecsThe ECS context
entityThe entity to destroy

◆ ecs_remove()

void ecs_remove ( ecs_t ecs,
ecs_entity_t  entity,
ecs_comp_t  comp 
)

Removes a component instance from an entity.

WARNING: This function may change the order of a system's entity array. It should be used with caution. A better option in most circumstances is to use the ecs_queue_remove function, which removes the component after the system has finished executing.

Parameters
ecsThe ECS context
entityThe entity
compThe component

◆ ecs_queue_destroy()

void ecs_queue_destroy ( ecs_t ecs,
ecs_entity_t  entity 
)

Queues an entity for destruction after the current system returns.

Queued entities are destroyed after the curent iteration.

Parameters
ecsThe ECS context
entityThe entity to destroy

◆ ecs_queue_remove()

void ecs_queue_remove ( ecs_t ecs,
ecs_entity_t  entity,
ecs_comp_t  comp 
)

Queues a component for removal from the specified entity.

Queued entity/component pairs that will be deleted after the current system returns.

Parameters
ecsThe ECS context
entityThe entity that has the component
compThe component to remove

◆ ecs_run_system()

ecs_ret_t ecs_run_system ( ecs_t ecs,
ecs_system_t  sys,
ecs_mask_t  mask 
)

Update an individual system.

Calls system logic on required components, but not excluded ones.

Parameters
ecsThe ECS context
sysThe system to update
maskBitmask that determines which systems run based on category.

◆ ecs_run_systems()

ecs_ret_t ecs_run_systems ( ecs_t ecs,
ecs_mask_t  mask 
)

Updates all systems.

Calls ecs_run_system on all components in order of system definition. In many cases it is better to call ecs_run_system as needed.

Parameters
ecsThe ECS context
maskBitmask that determines which systems run based on category.