SkillAgentSearch skills...

Morpeh

🎲 ECS Framework for Unity Game Engine and .Net Platform

Install / Use

/learn @scellecs/Morpeh

README

<p align="center"> <img src="Scellecs.Morpeh/Unity/Utils/Editor/Resources/logo.png" width="260" height="260" alt="Morpeh"> </p>

Morpeh License Unity Version

🎲 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.

<details> <summary>Open Unity Package Manager and add Morpeh URL. </summary>

installation_step1.png
installation_step2.png

</details>

    ⭐ 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 logic
  • ISystem - main system that executes every frame in Update. Used for main game logic and data processing
  • IFixedSystem - system that executes in FixedUpdate with fixed time step
  • ILateSystem - system that executes in LateUpdate, after all Updates. Useful for logic that should run after main updates
  • ICleanupSystem - 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
View on GitHub
GitHub Stars642
CategoryDevelopment
Updated3d ago
Forks52

Languages

C#

Security Score

100/100

Audited on Mar 26, 2026

No findings