TrimKit.EntityOrchestration provides a lightweight, dependency-free way to manage entities and systems in a structured and deterministic manner - bridging the gap between classic OOP approach and modern ECS patterns.
You might ask, But isn't this just another ECS implementation?
Not quite - there's no "C" here. You work with traditional inheritance hierarchies and rich object models, just like in classic OOP design.
The problem with that traditional approach, however, is scalability: once your project grows, reasoning about behavior and dependencies quickly becomes painful. ECS frameworks solve this by enforcing rigid data decomposition - but at the cost of constant micromanagement, boilerplate, and a steep learning curve.
In short, Entity Orchestration aims to strike the balance somewhere in the middle.
It gives you rigid structure, determinism, and modularity of an ECS-style approach - but without forcing your game into hundreds of micro-components. You keep your natural inheritance hierarchies, while gaining predictable update flow and a unified orchestration layer that keeps everything tidy and maintainable.
- Deterministic and order-based system execution
- Strict or flexible entity categorization
- Type-safe queries for entities by registered types
- Collections can be modified during execution freely
- No external dependencies
Use provided nuget package or download the source.
🔧 dotnet add package TrimKit.EntityOrchestration
First, create the entity manager and register types you want to separate entities into (this also allows you to query them by type).
var entityManager = new EntityManager(strict: true)
.RegisterType<Character>()
.RegisterType<Player>() // inherits from Character
.RegisterType<Enemy>() // Inherits from Character
.RegisterType<Prop>();
entityManager.Initialize();Add and query entities.
// add some entities
entityManager.Add(new Player("Player 1"));
entityManager.Add(new Enemy("Rat"));
entityManager.Add(new Enemy("Orc"));
entityManager.Add(new Prop("Rock"));
// get entities of type Player (in our case just one)
var players = entityManager.GetByType<Player>();
// this will return two enemies
var enemies = entityManager.GetByType<Enemy>();
// this will return both: player AND enemies, since they both inherit from Character
var characters = entityManager.GetByType<Character>();Now, let's use EntityManager together with Orchestrator.
First, let's create a system.
public class MovementSystem : BaseSystem
{
public override void OnUpdate(EntityManager em, GameContext ctx)
{
// example logic
var players = em.GetByType<Player>();
// ...
}
}And then register it.
// we create orchestrator with GameContext and pass our existing entityManager into the constructor
var orchestrator = new Orchestrator<GameContext>(entityManager);
// next we can define some systems and initialize
orchestrator.AddSystem<MovementSystem>()
.AddSystem<CombatSystem>()
.AddSystem<AnotherSystem>()
.AddSystem<WhateverSystem>()
.Initialize();Finally, simply call Update in your game loop and pass your game context, so systems can access what they need.
orchestrator.Update(gameContext);| Method / Property | Description |
|---|---|
EntityManager(bool strict = false) |
Creates a new manager; in strict mode only registered types are allowed. |
EntityManager RegisterType<T>() |
Registers a base entity type for categorization. |
void Initialize() |
Locks registration and enables runtime operations. |
void Add(BaseEntity entity) |
Adds an entity and associates it with matching registered types. |
void Remove(BaseEntity entity) |
Removes an entity from all collections. |
void RemoveDeleted() |
Removes entities marked as deleted. |
void RemoveAll() |
Clears all entities. |
IReadOnlyList<BaseEntity> GetAll(bool excludeDeleted = true) |
Returns all entities. |
IReadOnlyList<T> GetByType<T>(bool excludeDeleted = true) |
Returns all entities of a specific type. |
IReadOnlyList<BaseEntity> GetOther(bool excludeDeleted = true) |
Returns entities that don't match any registered type. |
int Count(bool excludeDeleted = true) |
Returns total entity count. |
| Method / Property | Description |
|---|---|
Orchestrator(EntityManager entityManager) |
Creates an orchestrator that manages systems sharing the same EntityManager. |
Orchestrator<TContext> AddSystem<TSystem>() |
Registers a new system via reflection. |
Orchestrator<TContext> AddSystem(BaseSystem system) |
Registers a new system with manual instance injection. |
Orchestrator<TContext> Initialize() |
Initializes all registered systems. |
void Update(TContext context) |
Executes all active systems sequentially. |
Orchestrator<TContext> SetSystemState<TSystem>(bool active) |
Enables or disables a specific system. |
bool IsInitialized |
Indicates whether the orchestrator is initialized. |
- The orchestrator executes systems in the order they are added.
- You must initialize both EntityManager and Orchestrator before using them.
- Deactivated systems (
Active = false) are automatically skipped. EntityManagerstrict mode ensures strong type control for clean separation and no entities getting into the "other" bucket.- You can actually use
EntityManagerseparately withoutOrchestrator.
This library is great for small to medium projects, but the fact of difference in performance compared to pure ECS is not lost on me. If you need to iterate over tens of thousands entities every frame and do that FAST you should stick with ECS. There's a reason ECS paradigm exists and is so popular - you just can't beat it in terms of performance. But for everything else... nah.
- v1.0 - Initial release.
This library is part of the TrimKit collection - a set of small, focused C# libraries that make game development more enjoyable by reducing the need for boilerplate code and providing simple reusable building blocks that can be dropped into any project.
- TrimKit.EventBus - Lightweight, mutation-safe event bus (event aggregator).
- TrimKit.GameSettings - JSON-based persistent settings manager.
- TrimKit.VirtualFileSystem - Unified file hierarchy abstraction to enable modding and additional content in games.
- TrimKit.StatDictionary - Simple character stat container for RPG or other games relying on stat heavy calculations.
- TrimKit.EntityOrchestration - Entity and system orchestration layer.
Each module is independent and can be used standalone or combined with others for a complete lightweight foundation.
Contributions are welcome!
You can start with submitting an issue on GitHub.
This library is released under the MIT License.