Morpeh
π² ECS Framework for Unity Game Engine and .Net Platform
Install / Use
/learn @scellecs/MorpehREADME
Morpeh

π² ECS Framework for Unity Game Engine and .Net Platform
- Simple Syntax.
- Plug & Play Installation.
- No code generation (yet).
- Structure-Based and Cache-Friendly.
Key points for people familiar with other ECS frameworks:
- Archetype-based, but stores data separately from archetypes. Archetypes are used only for filtering, and there's no archetype transition graph, which means you should not worry about performance degradation over time for lots of chaotically changing entities.
- Components are stored in "stashes", which are also known as "pools" in other frameworks.
- Filters are used to select entities based on component types, but unlike other frameworks they should not be used in-place, and should rather be declared in the system OnAwake and stored in a variable.
- Filters directly store archetypes, and you can know if the filter is empty or not without iterating over it, possibly reducing systems idle time, especially in the case of a large number of systems, which is generally useful for Unity's IL2CPP.
- Designed to work well with a large quantity of different component types and systems in a single world (several thousands of component types and systems), but not necessarily with a large number of entities.
- Designed to work well in a large team with a large codebase, reducing the amount of things a typical developer should remember and account for. You are free to migrate entities by doing structural changes even if an entity contains tens of different components without a significant performance impact.
π Table of Contents
πΈ Migration To New Version
English version: Migration Guide
Russian version: ΠΠ°ΠΉΠ΄ ΠΏΠΎ ΠΌΠΈΠ³ΡΠ°ΡΠΈΠΈ
π How To Install
Unity Engine
Minimal required Unity Version is 2020.3.*
Requires Git for installing package.
Requires Tri Inspector for drawing the inspector.


Β Β Β Β β Master: https://github.com/scellecs/morpeh.git?path=Scellecs.Morpeh
Β Β Β Β π§ Stage: https://github.com/scellecs/morpeh.git?path=Scellecs.Morpeh#stage-2024.1
Β Β Β Β π·οΈ Tag: https://github.com/scellecs/morpeh.git?path=Scellecs.Morpeh#2024.1.1
.Net Platform
NuGet package URL: https://www.nuget.org/packages/Scellecs.Morpeh
π Introduction
π Base concept of ECS pattern
π Entity
An identifier for components, which does not store any data but can be used to access components. Logically, it is similar to a GameObject in Unity, but an Entity does not store any data itself.
It is a value type, and is trivially copyable. Underlying identifiers (IDs) are reused, but each reused ID is guaranteed to have a new generation, making each new Entity unique.
var healthStash = this.World.GetStash<HealthComponent>();
var entity = this.World.CreateEntity();
ref var addedHealthComponent = ref healthStash.Add(entity);
ref var gottenHealthComponent = ref healthStash.Get(entity);
//if you remove the last entity component, it will be destroyed during the next world.Commit() call
bool removed = healthStash.Remove(entity);
healthStash.Set(entity, new HealthComponent {healthPoints = 100});
bool hasHealthComponent = healthStash.Has(entity);
var debugString = entity.ToString();
//remove entity
this.World.RemoveEntity(entity);
//check disposal
bool isDisposed = this.World.IsDisposed(entity);
//alternatively
bool has = this.World.Has(entity);
π Component
Components are types which store components data. In Morpeh, components are value types for performance purposes.
public struct HealthComponent : IComponent {
public int healthPoints;
}
π System
Types that process entities with a specific set of components.
Entities are selected using a filter.
All systems are represented by interfaces.
public class HealthSystem : ISystem {
public World World { get; set; }
private Filter filter;
private Stash<HealthComponent> healthStash;
public void OnAwake() {
this.filter = this.World.Filter.With<HealthComponent>().Build();
this.healthStash = this.World.GetStash<HealthComponent>();
}
public void OnUpdate(float deltaTime) {
foreach (var entity in this.filter) {
ref var healthComponent = ref healthStash.Get(entity);
healthComponent.healthPoints += 1;
}
}
public void Dispose() {
}
}
All systems types:
IInitializer- has OnAwake and Dispose methods only, which is convenient for executing startup logicISystem- main system that executes every frame in Update. Used for main game logic and data processingIFixedSystem- system that executes in FixedUpdate with fixed time stepILateSystem- system that executes in LateUpdate, after all Updates. Useful for logic that should run after main updatesICleanupSystem- system that executes after ILateSystem. Designed for cleanup operations, resetting states, and handling end-of-frame tasks
Beware that ScriptableObject-based systems do still exist in 2024 version, but they are deprecated and will be removed in the future.
π SystemsGroup
The type that contains the systems. Consider them as a "feature" to group the systems by their common purpose.
var newWorld = World.Create();
var newSystem = new HealthSystem();
var newInitializer = new HealthInitializer();
var systemsGroup = newWorld.CreateSystemsGroup();
systemsGroup.AddSystem(newSystem);
systemsGroup.AddInitializer(newInitializer);
//it is a bad practice to turn systems off and on, but sometimes it is very necessary for debugging
systemsGroup.DisableSystem(newSystem);
systemsGroup.EnableSystem(newSystem);
systemsGroup.RemoveSystem(newSystem);
systemsGroup.RemoveInitializer(newInitializer);
newWorld.AddSystemsGroup(order: 0, systemsGroup);
newWorld.RemoveSystemsGroup(systemsGroup);
π World
A type that contains entities, components stashes, systems and root filter.
var newWorld = World.Create();
//a variable that specifies whether the world should be updated automatically by the game engine.
//if set to false, then you can update the world manually.
//and can also be used for game pauses by changing the value of this variable.
newWorld.UpdateByUnity = true;
var newEntity = newWorld.CreateEntity();
newWorld.RemoveEntity(newEntity);
var systemsGroup = newWorld.CreateSystemsGroup();
systemsGroup.AddSystem(new HealthSystem());
newWorld.AddSystemsGroup(order: 0, systemsGroup);
newWorld.RemoveSystemsGroup(systemsGroup);
var filter = newWorld.Filter.With<HealthComponent>();
var healthStash = newWorld.GetStash<HealthComponent>();
var reflectionHealthStash = newWorld.GetReflectionStash(typeof(HealthComponent));
//manually world updates
newWorld.Update(Time.deltaTime);
newWorld.FixedUpdate(Time.fixedDeltaTime);
newWorld.LateUpdate(Time.deltaTime);
newWorld.CleanupUpdate(Time.deltaTime);
//apply all entity changes, filters will be updated.
//automatically invoked between systems
newWorld.Commit();
π Filter
A type that allows filtering entities constrained by conditions With and/or Without.
You can chain them in any order and quantity.
Call Build() to finalize the filter for further use.
var filter = this.World.Filter.With<HealthComponent>()
.With<BooComponent>()
.Without<DummyComponent>()
.Build();
var firstEntityOrException = filter.First();
var firstEntityOrNull = filter.FirstOrDefault();
bool filterIsEmpty = filter.IsEmpty();
bool filterIsNotEmpty = filter.IsNotEmpty();
int filterLengthCalculatedOnCall = filter.GetLengthSlow();
π Stash
A type that stores components data.
var healthStash = this.World.GetStash<HealthComponent>();
var entity = this.World.CreateEntity();
ref var addedHealthComponent = ref healthStash.Add(entity);
ref var gottenHealthComponent = ref healthStash.Get(entity);
bool removed = healthStash.Remove(entity);
healthStash.Set(entity, new HealthComponent {healthPoints = 100});
bool hasHealthComponent = healthStash.Has(entity);
//delete all HealthComponent from the world (affects all entities)
healthStash.RemoveAll();
bool healthStashIsEmpty = healthStash
