EathECS. An ECS with a focus on ease of use.
Core of the system is sparse set ecs implementation with some C++ fold expressions to make queries and systems work naturally. Components are identified by their "name hash" instead of type, so essentially eecs allows you to have any number of components of the same type.
To use eecs you just need to #include "eath_ecs.h" in your files and that should be it. All functions are inlined and are accessible by your code.
If you want to build samples, you can go the cmake route:
cmake -B build
cmake --build build
And then run executables built in build/ directory.
- Shrink SparseSets
- More examples:
- Deletion of entities
- Deletion of components
- Query from inside another query or system
- Somehow (tm) figure out how to fix component and lambda arguments duplication?
- Better description
- Serialization
- Script integration (?)
- Tests!
Take a look at the samples folder, they're concise, but generally you have to
- Create your registry somewhere:
eecs::Registry reg; - Create an entity:
eecs::EntityId eid = create_entity(reg); - Set value for components:
set_component(reg, eid, COMPID(float, position), 0.f); - Or maybe you want to utilize entity wrap (so you can use
.set()function to set components):
create_entity_wrap(reg)
.set(COMPID(float, position), 1.f)
.set(COMPID(float, velocity), 0.f);- Query single component:
eecs::query_component(reg, eid, [&](const float& position)
{
printf("Component value is %.2f\n", position);
}, COMPID(float, position));- Query multiple components:
eecs::query_components(reg, eid, [&](const float& position, float velocity)
{
printf("Position %.2f, Velocity %.2f\n", position, velocity);
}, COMPID(float, position), COMPID(float, velocity));- Query all entities with components:
eecs::query_entities(reg,
[&](eecs::EntityId eid, float position, float velocity)
{
printf("Entity(%d): pos (%.2f), vel (%.2f)\n", eid, position, velocity);
}, COMPID(float, position), COMPID(float, velocity));- Register a system:
eecs::reg_system(reg, [&](eecs::EntityId eid, float& position, float velocity)
{
position += velocity * dt;
}, COMPID(float, position), COMPID(float, velocity));-
And step all systems in registry:
eecs::step(reg);- Systems are executed in the order of their registration, there's no way to reorder systems (yet?)
-
If you want to use prefabs to reuse some of the components, it's easy to do so too:
- Create a prefab either via
eecs::EntityId myPrefab = eecs::create_prefab(reg);or via a wrapeecs::EntityWrap myPrefab = eecs::create_prefab_wrap(reg); - Add components to your prefab:
myPrefab.set(COMPID(float, timer), 0.f).set(COMPID(int, experience), 0); - Then clone your entity from that prefab:
eecs::create_wrap_from_prefab(reg, myPrefab) - And you're good to go
- You can make prefabs from prefabs, or make any entity a prefab via
toprefab()function in theEntityWrapor via standalonemake_prefab(reg, eid)
- Create a prefab either via

eecs (from flecs) as a proof of concept that this library works in practice at
least on a small scale project. Functionaly it uses all current features of eecs and essentially was a
testing ground for the library.
One of the main reasons for the rewrite was to allow named component IDs, so across 24 systems and over 30 queries there is only
one component which is not considered a simple struct or type like float or Vector2. This allowed to get rid of a lot of code
which was just adding burden to the system.

eecs from the start. It was an additional proof that library is mature enough to make simple games fast.

It utilizes eecs heavily and all levels are done via a serialization of saveable entities (entities with a tag Saveable), as well as save/load functionality.
eecs allowed the game to be more data driven (some components are not represented in the code at all, used as dynamic tags to dynamically query entities) and utilized edat as for data storage and serialization.