Repository navigation
Home
Sia is an Entity Component System (ECS) for .NET. Components hold data, entities group components, and systems process matching entities.
This wiki describes the current Sia.NET API, targeting .NET 10. Keep the runtime and source-generator versions aligned.
| Term | Plain-language meaning |
|---|---|
World |
Owns component storage, entities, events, addons, and queries. |
Entity |
A pooled handle to one item in a world. |
| Component | A small value such as position, velocity, or health. |
| Query | Selects entities containing required components. |
| System | Runs behavior over a query. |
Reference the Sia project, then run:
using System;
using Sia;
file static class Program
{
private record struct Position(float X, float Y);
private record struct Velocity(float X, float Y);
public static void Main()
{
using var world = new World();
var entity = world.Create(HList.From(
new Position(0, 0),
new Velocity(2, 1)));
world.Query(Matchers.Of<Position, Velocity>())
.ForSlice(static (ref Position position, ref Velocity velocity) => {
position.X += velocity.X;
position.Y += velocity.Y;
});
Console.WriteLine(entity.Get<Position>());
entity.Destroy();
}
}The world stores the entity in an archetype containing Position and Velocity. The query selects that archetype and updates both components by reference.
| API | Purpose |
|---|---|
ISystem, SystemBase
|
Matcher, trigger/filter, lifecycle, execution, and child systems. |
SystemChain |
Immutable system composition and configuration. |
SystemDescriptor |
Set membership and Before/After dependencies. |
SystemStage |
Planned runtime stage created for one world. |
Scheduler |
Dependency-ordered labeled schedules and lifecycle callbacks. |
FSystem |
Functional component callbacks with optional runner/barrier. |
var chain = SystemChain.Empty
.Add<InputSystem>()
.Add<MovementSystem>()
.Configure<MovementSystem>(descriptor =>
descriptor.After<InputSystem>());
using var stage = chain.CreateStage(world);
stage.Tick();- A matcher without triggers processes the full query each tick.
- A trigger union, optionally with a filter union, creates an event-collected reactive system; collected entities are deduplicated and cleared after execution.
- Planning uses stable dependency ordering. Missing targets create no edge;
cycles throw
SystemCycleException. - The core scheduler pipeline is
First -> PreUpdate -> Update -> PostUpdate -> Last;Startupis manual. -
WithRunner(ParallelRunner.Default)is asynchronous unless followed byWithBarrier(). - Dispose stages before their world.
A world mutated or ticked from more than one thread needs a WorldActor:
bind it once, and every other thread posts work into its mailbox instead of
calling Send/Execute directly.
using var actor = new WorldActor(world);
var ownerThread = Task.Run(() => actor.Run());
actor.PostTickSchedule(scheduler, CoreSchedules.Update);
actor.CompleteAndDispose(TimeSpan.FromSeconds(5));
ownerThread.Wait();ActorRuntime shares one worker pool across many actors instead of a
dedicated thread per world. See Architecture for the mailbox model.
- Create the complete initial component set with
HList.From(...)when possible. - Check
Contains<T>()before optionalGet<T>()access. - Do not retain an entity after
Destroy()or cache its slot. - Do not add, remove, or destroy entities while iterating their host.
- Pass
worldexplicitly to generated views and commands. - Dispose stages and worlds deterministically.
- Do not mutate a world bound to a
WorldActorfrom any thread but its own; post the work instead.
Read Code Generators for observable component properties, Prelude for optional indexes and hierarchies, Reactive for reducer-driven composition, and Architecture for the compact internal model.