Helios Engine
A modular ECS based data-oriented C++23 game engine framework
Loading...
Searching...
No Matches
ecs Directory Reference

Directories

 
include
 
src

Detailed Description

`ecs` — Entity Component System

Data-oriented ECS with deferred commands, double-buffered messages, archetype-based component storage, and parallel schedule execution. The heart of the engine.

Public API

Core Types

Type Purpose
World Owns entities, components, resources, messages, and the global command queue.
WorldView Read-only, thread-safe projection of a const World&.
Entity 64-bit ID: 32-bit index + 32-bit generation.
Schedule System collection with ordering, access policies, and executor selection.
Scheduler Groups schedules into stages; builds and runs them.
Commands Deferred mutation queue for systems (Spawn, Entity, World).
Query<Args...> Entity iteration with component access and With<> / Without<> filters.
Res<T> Resource access (Res<const T> for read-only).
Local<T> Per-system local storage (never conflicts in scheduling).
MessageWriter<T> Write regular messages (current frame buffer).
MessageReader<T> Read regular messages (previous frame buffer).
AsyncMessageWriter<T> / AsyncMessageReader<T> Lock-free async message queue.

World Lifecycle

Method Effect
Flush() Flush reserved entities + execute global commands. No message lifecycle change.
Update() Flush() + advance message lifecycle (swap buffers, clear aged messages).
Clear() Remove all world data.

World::Update() is called once per frame after the update stage completes (handled by app::Scheduler).

Quick Start

struct Position { float x = 0, y = 0; };
struct Velocity { float dx = 1, dy = 0; };
struct Player {};
// Components
auto entity = world.CreateEntity();
world.AddComponents(entity, Player{}, Position{}, Velocity{});
// Resources
world.InsertResources(GameConfig{.speed = 1.0F});
// Query
auto movables = world.Query<Position&, const Velocity&, helios::ecs::With<Player>>();
movables.ForEach([](Position& pos, const Velocity& vel) {
pos.x += vel.dx;
pos.y += vel.dy;
});
world.Update();
The World class manages entities with their components and systems.
Definition world.hpp:39
auto Query(Allocator alloc={}) noexcept -> BasicQuery< World, Allocator, Args... >
Creates a query over entities matching the specified component criteria.
Definition world.hpp:162
void AddComponents(Entity entity, Ts &&... components)
Adds components to the entity.
Definition world.hpp:972
void Update()
Flushes buffer of pending operations.
Definition world.hpp:894
Entity CreateEntity()
Creates new entity.
Definition world.hpp:923
void InsertResources(T &&resource)
Inserts a resource into the world.
Definition world.hpp:1126

Systems & Schedules

Systems declare data access through parameter types. The schedule auto-deduces access policies and builds a parallel execution DAG.

struct MoveSystem {
void operator()(helios::ecs::Res<const GameConfig> config,
if (config->paused) return;
movables.ForEach([&config](Position& pos, const Velocity& vel) {
pos.x += vel.dx * config->speed;
pos.y += vel.dy * config->speed;
});
}
};
helios::ecs::Schedule schedule("Update");
schedule.Add(MoveSystem{});
helios::async::Executor async_executor;
schedule.SetExecutor(
std::make_unique<helios::ecs::MultiThreadedExecutor>(async_executor));
schedule.Build();
schedule.RunAndWait(world);
Manages worker threads and executes task graphs using work-stealing scheduling.
Definition executor.hpp:32
void ForEach(const Action &action) const
Executes an action for each matching entity's components.
Definition query.hpp:1408
Read-only or mutable access to resource.
Definition param.hpp:17
A collection of systems, sets, and run conditions with ordering constraints.
Definition schedule.hpp:182
BasicQuery< World, std::pmr::polymorphic_allocator<>, Args... > Query
Alias for BasicQuery bound to World with a PMR allocator.
Definition query.hpp:2264

System Parameters

Parameter Access
Res<T> / Res<const T> Mutable / read-only resource
Query<Args...> Entity iteration (T&, const T&, const T*, With<T>, Without<T>)
Commands Deferred spawn/destroy/component/resource mutations
Local<T> Per-system persistent state
MessageWriter<T> Write messages (visible next frame)
MessageReader<T> Read messages (from previous frame)

Custom System Parameters

Built-in parameters (Res, Query, Commands, …) are wired through SystemParamTraits in helios/ecs/system/param_traits.hpp. User types extend the same mechanism by specializing SystemParamTraits<T> in namespace helios::ecs.

A type models the SystemParam concept when the specialization provides:

Method Purpose
RegisterAccess(AccessPolicyBuilder&) Declare component/resource accesses for parallel scheduling
Make(World&, SystemLocalData&, const AccessPolicy&) Construct the parameter at system invocation time

Commands, Local<T>, WorldView, and message params register no scheduling access — they never participate in conflict detection.

Aggregate parameters (CompositeSystemParam)

Bundle multiple built-in params into one struct; field types must match the template list exactly:

struct Mesh {};
struct Transform { float x = 0; };
struct Camera { float fov = 90.0F; };
struct RenderParam {
helios::ecs::Res<const Camera> camera;
};
namespace helios::ecs {
template <>
struct helios::ecs::SystemParamTraits<RenderParam>
: helios::ecs::CompositeSystemParam<RenderParam,
helios::ecs::Res<const Camera>,
helios::ecs::Query<const Mesh&, const Transform&>
> {};
} // namespace helios::ecs
struct RenderSystem {
void operator()(RenderParam param) {
param.meshes.ForEach([&](const Mesh&, const Transform& t) {
// use param.camera->fov and t.x
});
}
};

Fully custom parameters

Implement SystemParamTraits directly when you need custom Make logic or non-standard access declaration:

struct Time { float delta = 0.0F; };
struct PhysicsWorld {};
struct PhysicsStepParam {
float dt = 0.0F;
};
namespace helios::ecs {
template <>
struct helios::ecs::SystemParamTraits<PhysicsStepParam> {
static PhysicsStepParam Make(helios::ecs::World& world,
helios::ecs::SystemLocalData& /*data*/,
const helios::ecs::AccessPolicy& /*policy*/) {
return PhysicsStepParam{
.dt = world.ReadResource<Time>().delta,
.world = Res<PhysicsWorld>(world.WriteResource<PhysicsWorld>())};
}
static constexpr void RegisterAccess(helios::ecs::AccessPolicyBuilder& builder) {
builder.ReadResources<Time>();
builder.WriteResources<PhysicsWorld>();
}
};
} // namespace helios::ecs
struct PhysicsSystem {
void operator()(PhysicsStepParam step) {
step.world->Simulate(step.dt);
}
};
constexpr auto ReadResources(this auto &&self) -> decltype(std::forward< decltype(self)>(self))
Declares read-only access to resource types.
constexpr auto WriteResources(this auto &&self) -> decltype(std::forward< decltype(self)>(self))
Declares write access to resource types.
const T & ReadResource() const noexcept
Gets const reference to a resource.
Definition world.hpp:1225
T & WriteResource() noexcept
Gets reference to a resource.
Definition world.hpp:1218

Use DeclareReadComponents, DeclareWriteComponents, or DeclareQueryAccess from helios/ecs/system/access_decl.hpp when you need finer-grained component declarations inside RegisterAccess.

After specialization, pass the type to Schedule::Add like any other system — access policy and parallel ordering are deduced automatically.

Ordering

struct SpawnSet {};
struct MovementSet {};
schedule.Add(SpawnSystem{}, MoveSystem{}).InSet(MovementSet{}).Sequence();
schedule.Set<SpawnSet>().Sequence();
schedule.Add(RenderSystem{}).Before<MoveSystem>();

Commands — Deferred Mutations

Systems never mutate the world directly during parallel execution. All structural changes go through Commands:

struct SpawnEnemies {
void operator()(helios::ecs::Commands commands) {
commands.Spawn()
.AddComponents(Enemy{}, Position{0, 0}, Velocity{1, 0});
}
};
struct Cleanup {
void operator()(helios::ecs::Query<Lifetime&> lifetimes,
helios::ecs::Commands commands) {
lifetimes.ForEachWithEntity([&](helios::ecs::Entity e, Lifetime& lt) {
if (lt.remaining <= 0) {
commands.Entity(e).Destroy();
}
});
}
};
void ForEachWithEntity(const Action &action) const
Executes an action for each entity and its components.
Definition query.hpp:1423
Thin wrapper over command queue and world for deferred ECS operations.
Definition commands.hpp:24
constexpr PmrEntityCmdBuffer Entity(Entity entity)
Returns a command buffer for an existing entity.
Definition commands.hpp:104
PmrEntityCmdBuffer Spawn()
Spawns a new entity and returns a command buffer for it.
Definition commands.hpp:99
auto AddComponents(this auto &&self, Ts &&... components) -> decltype(std::forward< decltype(self)>(self))
Enqueues a command to add components to the entity.
auto Destroy(this auto &&self) -> decltype(std::forward< decltype(self)>(self))
Enqueues a command to destroy entity and removes it from the world.

Commands apply at schedule boundaries (RunAndWaitFlush() → per-system local flush). Split spawning and consumption across schedules (e.g. kPreUpdatekUpdate) when entities must exist before queries run.

Messages

Regular messages use a double-buffer with a 2-frame TTL:

Frame N: write via MessageWriter<T>
Frame N+1: read via MessageReader<T>
Frame N+2: auto-cleared (unless kManual clear policy)
struct DamageEvent {
float amount = 0;
};
world.AddMessage<DamageEvent>();
struct ApplyDamage {
events.ForEach([&](const DamageEvent& evt) {
if (auto* hp = healths.Get(evt.target)) {
hp->value -= evt.amount;
}
});
}
};
auto Get(Entity entity) const -> typename BasicQuery< WorldT, Allocator, Args... >::value_type
Gets all query components for a specific entity (ignores query filters).
Definition query.hpp:1264
Unique identifier for entities with generation counter to handle recycling.
Definition entity.hpp:22
constexpr void ForEach(const Action &action) const
Applies an action to every message (unwrapped const T&).
Definition reader.hpp:317
Type-safe, zero-copy reader for regular messages.
Definition reader.hpp:771
void AddMessage()
Registers an message type for use.
Definition world.hpp:1232

Async messages (kAsync = true) use a lock-free queue and are not managed by World::Update() — clear explicitly.

Components

Two storage models, chosen per component type:

Model Default for Behavior
Archetype Non-empty components SoA columns; add/remove migrates between archetypes.
Sparse set Tag/empty components Independent per-type storage; no archetype migration.

Override with static constexpr ComponentStorageType kStorageType = ... in the component struct.

Queries

// Mutable + const + optional pointer + filters
auto q = world.Query<Transform&, const Mesh&, const Light*,
helios::ecs::With<Visible>, helios::ecs::Without<Hidden>>();
q.ForEach([](Transform& t, const Mesh& mesh, const Light* light) { /* ... */ });
q.ForEachWithEntity([](helios::ecs::Entity e, Transform& t) { /* ... */ });
// Lazy adapters: Filter, Map, Take, Skip, Enumerate, Chain, Zip, ...
// Terminals: Collect, Fold, Find, Count, Any, All, None, GroupBy, ...

Executors

Kind Behavior
MainThreadExecutor Sequential on calling thread
SingleThreadedExecutor Sequential on one background thread
MultiThreadedExecutor Parallel via async::Executor work-stealing

Builtin Types

  • Commands: DestroyEntityCmd, AddComponentsCmd, InsertResourceCmd, FunctionCmd, … (builtin_commands.hpp)
  • Messages: EntityAddedMsg, EntityDestroyedMsg, ComponentAddedMsg<T>, ResourceInsertedMsg<T>, … (builtin_messages.hpp)

Dependencies

  • async — parallel schedule execution
  • container — sparse sets, typed buffers, multi-type maps
  • core, compiler, memory, utils, log
  • External: Boost flat_map, moodycamel ConcurrentQueue