ECS - breaking changes

Updates. They can be painful, but will help alot for moving forward in development. Time to break something in my ECS framework.

Updates. They can be painful, but will help alot for moving forward in development. Time to break something in my ECS framework.

Changes

  • Name. Now it named as "LeoECS". Yes, it’s stupid, but required for using in articles / discussions.
  • No more filter events and Reactive systems based on them. Yes, bad idea for some users, but it will help to optimize performance and decrease code size.
  • Filters switched to generics versions. It’s most breakable back compatibility thing, but it helps to decrease magic inside and decrease user code size:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // before
    [EcsFilterInclude(typeof(Component1))]
    EcsFilter _filter1;

    [EcsFilterInclude(typeof(Component1), typeof(Component2))]
    [EcsFilterExclude(typeof(Component3))]
    EcsFilter _filter2;

    // after
    EcsFilter<Component1> _filter1;

    EcsFilter<Component1, Component2>.Exclude<Component3> _filter2;

    Filters supports up to 5 components as Include constraint and up to 2 components as Exclude constraint. If you need more, well, you can request it through github issue, but looks like you already have problems with project’s architecture.

  • Components automatically will be added to typed arrays at filter: Components1, Components2, etc. Type of each component array based on order and type of requested component in filter constraint:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    EcsWorld _world;
    EcsFilter<MyComponent1> _filter1;
    EcsFilter<MyComponent1, MyComponent2> _filter2;

    // EntitiesCount should be used for walk over valid number of enitities in filter.
    for (var i = 0; i < _filter1.EntitesCount; i++) {
    // int[] Entities array contains entity ID-s.
    int entity = _filter1.Entities[i];

    // You can use standard method for getting component.
    MyComponent1 component1 = _world.GetComponent<Component1> (entity);

    // But better to use new way to do same thing.
    // MyComponent1[] Components1 array contains instances from filtered entities.
    MyComponent1 component11 = _filter1.Components1[i];

    // component1 and component11 are equals - no need to write additional code and waste performance.
    }
    for (var i = 0; i < _filter2.EntitesCount; i++) {
    // We can get first component from filter constraint - Component1 typed.
    MyComponent1 component1 = _filter2.Components1[i];
    // We can get second component from filter constraint - Component2 typed.
    MyComponent2 component2 = _filter2.Components2[i];
    }

    If you don’t need force filling components in filter (for flag-based components for example), EcsIgnoreInFilter attribute can be used:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class Component1 { }

    [EcsIgnoreInFilter]
    class Component2 { }

    [EcsInject]
    class TestSystem : IEcsSystem {
    EcsFilter<Component1, Component2> _filter;

    public Test() {
    for (var i = 0; i < _filter.EntitiesCount; i++) {
    // its valid code.
    var component1 = _filter.Components1[i];

    // its invalid code due to _filter.Components2 is null for memory / performance reasons.
    var component2 = _filter.Components2[i];
    }
    }
    }
  • Dependency Injection. Its optional and disabled by default. No more EcsWorld, EcsFilterInclude and EcsFilterExclude attribute. Now - its only one EcsInject attribute for system class:

    1
    2
    3
    4
    5
    6
    [EcsInject]
    class MySystem : IEcsSystem {
    EcsWorld _world;
    EcsFilter<MyComponent1> _filter1;
    EcsFilter<MyComponent2>.Exclude<Component3> _filter2;
    }

    All ecs related fields will be initialized correctly as before.

I hope, changes were not so aweful and allows you to write more clean and performant code.