ECS Public API
How to use Nevo's entity-component-system from game code.
Core Model
Nevo uses a data-oriented ECS:
- Entity - a
uint32_tID. - Component - a plain data struct.
- System - logic that runs over entities matching a component signature.
- Coordinator - the public API for entity, component, and system operations.
Include the public coordinator and component headers:
#include <EngineExports.hpp>
#include <ECS/Coordinator.hpp>
#include <ECS/components/Transform.hpp>Get the coordinator from the engine:
auto& engine = Public::Engine::getInstance();
auto& coord = engine.GetCoordinator();Entities
Public::ECS::EntityID entity = coord.createEntity();
if (entity == Public::ECS::INVALID_ENTITY) {
LOG_ERROR("Could not create entity");
}Useful entity APIs:
| API | Use |
|---|---|
createEntity() | Allocate a new entity ID. |
createEntity(entityID) | Create a specific ID, used by scene loading. |
destroyEntity(entity) | Destroy the entity, remove its components, and remove it from systems. |
clearAllEntities() | Destroy every active entity. |
getEntityCount() | Return active entity count. |
Components
Register component types before adding them:
coord.registerComponent<MyComponent>();Add, remove, query, and read components:
coord.addComponent(entity, MyComponent{});
if (coord.hasComponent<MyComponent>(entity)) {
auto component = coord.getComponent<MyComponent>(entity);
if (component) {
component->get().speed = 8.0f;
}
}
coord.removeComponent<MyComponent>(entity);getComponent<T> returns std::optional<std::reference_wrapper<T>>, so you can mutate the component through component->get().
For batch work:
for (auto entity : coord.getEntitiesWithComponent<Transform>()) {
// IDs with Transform
}
for (auto& transform : coord.getComponentView<Transform>()) {
// Dense component span
}Systems
Systems inherit from Public::ECS::System and implement:
class SpinSystem : public Public::ECS::System {
public:
void update(float dt) override;
void entityAdded(Public::ECS::EntityID entity) override;
void entityRemoved(Public::ECS::EntityID entity) override;
};Register the system and set its signature:
auto spin = coord.registerSystem<SpinSystem>();
Public::ECS::ComponentSignature signature;
signature.set(coord.getComponentType<Transform>());
signature.set(coord.getComponentType<Spin>());
coord.setSystemSignature<SpinSystem>(signature);The system receives entities that contain every component in its signature. The update order is the order systems were registered.
Iterating Entities
Use entityList() for a stable vector-style view when iteration performance matters:
void SpinSystem::update(float dt) {
for (auto entity : entityList()) {
// read/write matching components
}
}The legacy entities set is still public, but entityList() avoids set iteration overhead.
Custom Scene Components
Scene JSON can create custom game components if you register a creator before engine.Initialize():
inline void CreateSpinComponent(Public::ECS::EntityID entity, const json& data) {
Spin spin{};
spin.speed = data.value("speed", 1.0f);
Public::Engine::getInstance().GetCoordinator().addComponent(entity, spin);
}
int main(int argc, char* argv[]) {
auto& engine = Public::Engine::getInstance();
engine.RegisterComponent("Spin", CreateSpinComponent);
engine.Initialize();
auto& coord = engine.GetCoordinator();
coord.registerComponent<Spin>();
}Then a scene can include:
{
"type": "Spin",
"data": {
"speed": 2.0
}
}Built-In Registrations
The engine registers built-in components during initialization, including Transform, MeshRenderer, Camera, Light, ShadowProjector, Animator, Rigidbody, colliders, and post-processing components.
Game code only needs to register its own custom components and systems.
