Home

Awesome

<div align="center">

friflo ECSย ย  splash

</div> <div align="center">

nuget codecov CI-Engine Demos C# API Discord Wiki

</div>

Friflo.Engine.ECS

The ECS for finishers ๐Ÿ
Leading performance in most ECS aspects.
Performance Ratio - see C# ECS Benchmark

frifloFlecs.NETTinyEcsArchfennecsLeopotamDefaultEcsMorpeh
Ratio1.002.553.426.9619.022.573.8121.09
Notes111

News

Contents

Feature highlights

Complete feature list at Wiki โ‹… Features.

Get package on nuget or use the dotnet CLI.

dotnet add package Friflo.Engine.ECS

Projects using friflo ECS

Horse Runner DX

<a href="https://store.steampowered.com/app/2955320/Horse_Runner_DX"><img src="https://raw.githubusercontent.com/friflo/Friflo.Engine-docs/main/docs/images/horse-runner-dx.png" width="246" height="124"/></a>
Quote from developer: "Just wanted to let you know that Friflo ECS 2.0.0 works like a charm in my little game.
I use it for basically everything (landscape segments, vegetation, players, animations, collisions and even the floating dust particles are entities).
After some optimization there is no object allocation during gameplay - the allocation graph just stays flat - no garbage collection."

Demos

MonoGame Demo is available as WASM / WebAssembly app. Try Demo in your browser.
Demo projects on GitHub below.

<table> <thead> <tr> <td><a href="https://github.com/friflo/Friflo.Engine.ECS-Demos/tree/main/MonoGame"><img src="https://raw.githubusercontent.com/friflo/Friflo.Engine-docs/main/docs/images/MonoGame-wasm.png" width="160px" height="100px"/></a></td> <td><a href="https://github.com/friflo/Friflo.Engine.ECS-Demos/tree/main/Unity" ><img src="https://raw.githubusercontent.com/friflo/Friflo.Engine-docs/main/docs/images/Unity.png" width="160px" height="100px"/></a></td> <td><a href="https://github.com/friflo/Friflo.Engine.ECS-Demos/tree/main/Godot" ><img src="https://raw.githubusercontent.com/friflo/Friflo.Engine-docs/main/docs/images/Godot.png" width="160px" height="100px"/></a></td> </tr> </thead> <tbody> <tr> <td align="center"><a href="https://github.com/friflo/Friflo.Engine.ECS-Demos/tree/main/MonoGame" >MonoGame</a></td> <td align="center"><a href="https://github.com/friflo/Friflo.Engine.ECS-Demos/tree/main/Unity" >Unity</a></td> <td align="center"><a href="https://github.com/friflo/Friflo.Engine.ECS-Demos/tree/main/Godot" >Godot</a></td> </tr> </tbody> <table>

Desktop Demo performance: Godot 202 FPS, Unity 100 FPS at 65536 entities.
All example Demos - Windows, macOS & Linux - available as projects for MonoGame, Unity and Godot.
See Demos ยท GitHub

ECS definition

An entity-component-system (ECS) is a software architecture pattern. See ECS โ‹… Wikipedia.
It is often used in the Gaming industry - e.g. Minecraft - and used for high performant data processing.
An ECS provide two strengths:

  1. It enables writing highly decoupled code. Data is stored in Components which are assigned to objects - aka Entities - at runtime.
    Code decoupling is accomplished by dividing implementation in pure data structures (Component types) - and code (Systems) to process them.

  2. It enables high performant system execution by storing components in continuous memory to leverage CPU caches L1, L2 & L3.
    It improves CPU branch prediction by minimizing conditional branches when processing components in tight loops.

<br/>

โฉ Examples

This section contains two typical use cases when using an ECS.
More examples are in the GitHub Wiki.

Examples - General
Explain fundamental ECS types like Entity, Component, Tag, Command Buffer, ... and how to use them.

Examples - Optimization
Provide techniques how to improve ECS performance.

๐Ÿš€ Hello World

The hello world examples demonstrates the creation of a world, some entities with components
and their movement using a simple ForEachEntity() call.

public struct Velocity : IComponent { public Vector3 value; }

public static void HelloWorld()
{
    var world = new EntityStore();
    for (int n = 0; n < 10; n++) {
        world.CreateEntity(new Position(n, 0, 0), new Velocity{ value = new Vector3(0, n, 0)});
    }
    var query = world.Query<Position, Velocity>();
    query.ForEachEntity((ref Position position, ref Velocity velocity, Entity entity) => {
        position.value += velocity.value;
    });
}

In case of moving (updating) thousands or millions of entities an optimized approach can be used.
See: Enumerate Query Chunks, Parallel Query Job and Query Vectorization - SIMD.
All query optimizations are using the same query but with different enumeration techniques.

<br/>

โŒ˜ Component Types

new in Friflo.Engine.ECS v3.0.0-preview.2

For specific use cases there is now a set of specialized component interfaces providing additional features.
Note: Newly added features do not affect the behavior or performance of existing features.

The specialized component types enable entity relationships, relations and full-text search.
Typical use case for entity relationships in a game are:

Use cases for relations:

Use case / ExampleComponent interface typeDescription
Entity RelationshipsLink ComponentA single link on an entity referencing another entity
Link RelationMultiple links on an entity referencing other entities
RelationsRelation ComponentAdd multiple components of same type to an entity
Search & Range queriesIndexed ComponentFull text search of component fields executed in O(1).<br/>Range queries on component fields having a sort order.

Big shout out to fennecs and flecs for the challenge to improve the feature set and performance of this project!

<br/>

โš™๏ธ Systems

Systems are new in Friflo.Engine.ECS v2.0.0

Systems in ECS are typically queries.
So you can still use the world.Query<Position, Velocity>() shown in the "Hello World" example.

Using Systems is optional but they have some significant advantages.

System features

public static void HelloSystem()
{
    var world = new EntityStore();
    for (int n = 0; n < 10; n++) {
        world.CreateEntity(new Position(n, 0, 0), new Velocity(), new Scale3());
    }
    var root = new SystemRoot(world) {
        new MoveSystem(),
    //  new PulseSystem(),
    //  new ... multiple systems can be added. The execution order still remains clear.
    };
    root.Update(default);
}
        
class MoveSystem : QuerySystem<Position, Velocity>
{
    protected override void OnUpdate() {
        Query.ForEachEntity((ref Position position, ref Velocity velocity, Entity entity) => {
            position.value += velocity.value;
        });
    }
}

A valuable strength of an ECS is establishing a clear and decoupled code structure.
Adding the PulseSystem below to the SystemRoot above is trivial.
This system uses a foreach (var entity in Query.Entities) as an alternative to Query.ForEachEntity((...) => {...})
to iterate the query result.

struct Pulsating : ITag { }

class PulseSystem : QuerySystem<Scale3>
{
    float frequency = 4f;
    
    public PulseSystem() => Filter.AnyTags(Tags.Get<Pulsating>());
    
    protected override void OnUpdate() {
        foreach (var entity in Query.Entities) {
            ref var scale = ref entity.GetComponent<Scale3>().value;
            scale = Vector3.One * (1 + 0.2f * MathF.Sin(frequency * Tick.time));
        }
    }
}

โฑ System monitoring

System performance monitoring is disabled by default.
To enable monitoring call:

root.SetMonitorPerf(true);

When enabled system monitoring captures

Realtime monitoring

In a game editor like Unity system monitoring is available in the ECS System Set component.

<details> <summary>Screenshot: <b>ECS System Set</b> component in Play mode</summary> <img src="docs/images/SystemSet-Unity.png" width="324" height="ddd"/> </details>

Log monitoring

The performance statistics available at SystemPerf.
To get performance statistics on console use:

root.Update(default);
Console.WriteLine(root.GetPerfLog());

The log result will look like:

stores: 1                  on   last ms    sum ms   updates  last mem   sum mem  entities
---------------------      --  --------  --------  --------  --------  --------  --------
Systems [2]                 +     0.076     3.322        10       128      1392
| ScaleSystem               +     0.038     2.088        10        64       696     10000
| PositionSystem            +     0.038     1.222        10        64       696     10000
on                  + enabled  - disabled
last ms, sum ms     last/sum system execution time in ms
updates             number of executions
last mem, sum mem   last/sum allocated bytes
entities            number of entities matching a QuerySystem
<br/>

๐Ÿ“– Wiki

The GitHub Wiki provide you detailed information about the ECS and illustrate them by examples.

<br/>

๐Ÿ ECS Benchmarks

ECS.CSharp.Benchmark - Common use-cases

Created a new GitHub repository ECS.CSharp.Benchmark - Common use-cases.
It compares the performance of multiple ECS projects with simple benchmarks.
So they can be used as a guide to migrate form one ECS to another.
See discussion of reddit announcement Post.

ECS.CSharp.Benchmark

Performance comparison using popular ECS C# benchmark on GitHub.
Two benchmarks - subset of GitHub โ‹… Ecs.CSharp.Benchmark + PR #38 running on a Mac Mini M2.

See Benchmark results.

<br/>

License

This project is licensed under LGPLv3.

Friflo.Engine.ECS
Copyright ยฉ 2024ย ย ย Ullrich Praetz - https://github.com/friflo

Footnotes

  1. Sparse Set based ECS projects. โ†ฉ โ†ฉ2 โ†ฉ3