All Projects → c0ffeeartc → EntitasGenericAddon

c0ffeeartc / EntitasGenericAddon

Licence: MIT license
Addon to Entitas that allows using generic methods instead of code generator and uses type inference to insure compile time correctness

Programming Languages

C#
18002 projects

Projects that are alternatives of or similar to EntitasGenericAddon

SpaceWar-ECS
A space war game made with ECS and JobSystem in Unity.
Stars: ✭ 26 (+52.94%)
Mutual labels:  ecs, entitas
Entitas Csharp
Entitas is a super fast Entity Component System (ECS) Framework specifically made for C# and Unity
Stars: ✭ 5,393 (+31623.53%)
Mutual labels:  ecs, entitas
Entitas-lua
No description or website provided.
Stars: ✭ 29 (+70.59%)
Mutual labels:  ecs, entitas
entitas-python
Entitas ECS implementation in Python.
Stars: ✭ 41 (+141.18%)
Mutual labels:  ecs, entitas
terraform-aws-ecs-cluster
Terraform module for building an ECS cluster in AWS
Stars: ✭ 42 (+147.06%)
Mutual labels:  ecs
aws-ecs-orb
An orb that simplifies deployment to Amazon's Elastic Container Service (ECS). Supports both EC2 and Fargate launch types.
Stars: ✭ 48 (+182.35%)
Mutual labels:  ecs
addon-chrony
chrony - Home Assistant Community Add-ons
Stars: ✭ 23 (+35.29%)
Mutual labels:  addon
rockgo
A developing game server framework,based on Entity Component System(ECS).
Stars: ✭ 617 (+3529.41%)
Mutual labels:  ecs
spock
Automatically perform git commits, pushes, and other cli actions when Statamic content changes
Stars: ✭ 96 (+464.71%)
Mutual labels:  addon
Freeplane-Jumper
A search box for Freeplane with on-the-fly filtering and full keyboard control. Quick navigation in your maps!
Stars: ✭ 19 (+11.76%)
Mutual labels:  addon
blender-terrain
Terrain import is now a part of the blender-osm addon. Get it for free at https://gumroad.com/l/blender-osm
Stars: ✭ 28 (+64.71%)
Mutual labels:  addon
ember-contextual-services
Services in Ember are scoped to the app as a whole and are singletons. Sometimes you don't want that. :) This addon provides ephemeral route-based services.
Stars: ✭ 20 (+17.65%)
Mutual labels:  addon
ember-app-shell
No description or website provided.
Stars: ✭ 23 (+35.29%)
Mutual labels:  addon
pfQuest
A Questhelper and Database Addon for World of Warcraft: Vanilla & TBC
Stars: ✭ 109 (+541.18%)
Mutual labels:  addon
RavEngine
A fast, easy to use C++20 3D game library for modern computers
Stars: ✭ 122 (+617.65%)
Mutual labels:  ecs
ember-cli-mentionable
An ember addon for facebook style @mentions and #hashtags
Stars: ✭ 13 (-23.53%)
Mutual labels:  addon
material-combiner-addon
Blender addon for material combining, uv bounds fixing
Stars: ✭ 309 (+1717.65%)
Mutual labels:  addon
easy lightmap
blender easy light map addon
Stars: ✭ 19 (+11.76%)
Mutual labels:  addon
terraform-ecs
Terraform ECS module
Stars: ✭ 15 (-11.76%)
Mutual labels:  ecs
cuba-component-forgot-password
A CUBA framework component that adds forgot password functionality to CUBA. It also offer a REST API to be consumed by third party applications or by JS clients.
Stars: ✭ 16 (-5.88%)
Mutual labels:  addon

EntitasGenericAddon

Addon to Entitas that allows using generic methods instead of code generator and uses type inference to ensure compile time correctness

This project is based on Entitas.Generic

Goal

Make Entitas extensible by separate dll

Main Concepts

  • Code Generator is optional

  • Type inference forces only valid type combinations during development and gives access only to needed methods

  • Interfaces serve as markers for type inference

    • IScope - base interface for context scope
    • Scope<T> - context scope of IComponent
    • IComponent - allows class to be managed by Entitas, gives access to Get<T> extension methods
    • ICompData - gives access to Add, Remove, Replace, Has (class componenents require ICopyFrom<TSelf>).
    • ICreateApply - alternative workflow for class components that doesn't require ICopyFrom<TSelf>
    • ICompFlag - gives access to Flag<T>, Is<T>
    • IUnique - provides context Add, Replace etc methods for unique components or flags
  • Generic Events Feature

    • IEvent_*<TScope, TComp> - interface marker for IComponent classes
    • IOn*<TScope, TComp> - interface to implement by listener classes
    • EventSystem_*<TScope, TComp> - event system classes
  • Struct components API method names end with underscore (Add_, Remove_, Replace_, Has_, Event_System_*_)

  • Manual EntityIndex registration

Installation

  • Install Entitas framework into your project (tested with Entitas v1.13.0 but more recent may be also compatible)
  • Install EntitasGenericAddon into your project:
    • Preferred. Without using Entitas generator - copy Entitas.Generic, Entitas.Generic.Events sources into Assets folder somewhere
    • Not recommended. With existing generated Contexts - copy Entitas.Generic, Entitas.Generic.Events sources into same assembly as generated Contexts class. For now it only adds new generic contexts, generated and generic context instances have different workflows. Improvements are welcome

Warning: Please test project on target devices as soon as possible, and then regularly to avoid any pitfalls and show stoppers.

Examples

For more examples see EntitasGenericAddon.Examples

Usage

public void Example()
{
    // initialization must be called before new Contexts( ) or accessing Contexts.sharedInstance
    Lookup_ScopeManager.RegisterAll( );  // <-- 1

    var contexts = Contexts.sharedInstance;  // <-- 2
    // var contexts = new Contexts(  ); // <-- 2. If generator is not used in project

    contexts.AddScopedContexts(  ); // <-- 3

    var game = contexts.Get<Game>( );

    var entity = game.CreateEntity( );
    entity.Add( Cache<Position>.I.Set(3f, 10f) );
    entity.Replace( new Position( 20f, 1f ) );
    entity.Has<Position>( );
    entity.Remove<Position>( );

    entity.Flag<Moving>( true );
    entity.Is<Moving>( );
    entity.Flag<Moving>( false );

    game.Flag<GameActive>( true );  // provides unique component interfaces
    var gameActiveEntity = game.GetEntity<GameActive>( );
}

Scope

public interface Game : IScope { }

Component

public struct B : IComponent, ICompData, Scope<Game>
{
    public int Value;
}

public sealed class A
        : IComponent
        , ICompData
        , ICopyFrom<A>  // Not needed for struct components
        , Scope<Game>
{
    public int Value;

    public A Set(int value)  // optional, allows using Cache<T>.I.Set(). Not needed for struct components
    {
        Value = value;
        return this;
    }

    public void CopyFrom(A other)  // Not needed for struct components
    {
        Value = other.Value;
    }
}

ICreateApply alternative workflow for class components doesn't require to implement ICopyFrom<T>.

It's still recommended to create Set method that ensures initializing all values even after refactoring(in rider it's as easy as writing ctorf, pressing tab and renaming method to Set)

// Step1
public sealed class A
        : IComponent
        , ICompData
        , ICreateApply
        , Scope<Game>
{
    public int Value;
    
    public A Set (value)  // Optional
    {
        Value = value;
        return this;
    }
}

// Step2
var comp = entity.Create<A>(  );
comp.Value = 1f;
entity.Apply( comp );

// or recommended using Set method
entity.Apply( entity.Create<A>(  )
    .Set( 1f ) );

FlagComponent

public sealed class FlagA : IComponent, ICompFlag, Scope<Game> { }

Matcher

Matcher<Entity<Game>>
    .AllOf(
        Matcher<Game, Position>.I,
        Matcher<Game, Velocity>.I )
    .AnyOf(
        Matcher<Game, Moving>.I )
    .NoneOf(
        Matcher<Game, Destroy>.I ) );

Events

// Step 1. Add event markers to components
public sealed class FlagA : IComponent, ICompFlag, Scope<Game>
    , IEvent_Any<Game, FlagA> // <---
{ }
public sealed class B : IComponent, ICompData, ICopyFrom<B>, Scope<Game>
    , IEvent_SelfRemoved<Game, B>  // <---
{
    // some code
}

    
// Step 2. Add event systems to Systems. This step could be automated in future
    systems.Add( new EventSystem_SelfRemoved<Game, B>(  ) );
    systems.Add( new EventSystem_Any<Game, FlagA>(  ) );


// Step 3. Inherit and implement event interface
public class Some
    : MonoBehaviour
    , IOnAny<Game, FlagA>
    , IOnSelfRemoved<Game, B>
{
private void OnAny( FlagA component, Entity<Game> entity, Contexts contexts )
{
    // some code
}

private void OnSelfRemoved( B component, Entity<Game> entity, Contexts contexts )
{
    // some code
}


// Step 4. Add listeners to entity
private void OnEnable()
{
    var contexts = Contexts.sharedInstance;
    var entity = contexts.Get<Game>( ).CreateEntity( );

    entity.Add_OnSelfRemoved( this );

    entity.Add_OnAny( this ); // subscribe
    entity.Remove_OnAny( this ); // unsubscribe

    // writing types explicitly is required when implicit inference is impossible
    entity.Add_OnAny<Game, FlagA>( this );
    entity.Remove_OnAny<Game, FlagA>(  ); // removes listener component
}
}

EventsFeature2

Alternative to original EventsFeature. Uses Action to subscribe/unsubscribe.

// Step 1. Add event markers to components. Same as with original EventsFeature
public sealed class FlagA : IComponent, ICompFlag, Scope<Game>
    , IEvent_Any<Game, FlagA>          // <---
    , IEvent_Self<Game, FlagA>         // <---
{ }
public struct CompB : IComponent, ICompData, Scope<Game>
    , IEvent_Any<Game, CompB>         // <---
    , IEvent_AnyRemoved<Game, CompB>  // <---
    , IEvent_Self<Game, CompB>        // <---
    , IEvent_SelfRemoved<Game, CompB> // <---
{
    // some code
}


// Step 2. Add event systems to Systems. New EventSystem must be created before calling Sub/Unsub, otherwise NullReferenceException will be thrown
    systems.Add( new EventSystem_Any2<Game,CompB>(  ) );
    systems.Add( new EventSystem_Any_Removed2<Game,CompB>(  ) );
    systems.Add( new EventSystem_Any_Flag2<Game,FlagA>(  ) );  // Flag, callback on True and False unlike original EventsFeature

    systems.Add( new EventSystem_Self2<Game,CompB>(  ) );
    systems.Add( new EventSystem_Self_Removed2<Game,CompB>(  ) );
    systems.Add( new EventSystem_Self_Flag2<Game,CompB>(  ) );  // Flag, callback on True and False unlike original EventsFeature


// Step 3. Subscribe/Unsubscribe callback
private void Awake()
{
    OnAny<Game,CompB>.I.Sub( OnCompB );
    OnAny_Removed<Game,CompB>.I.Sub( OnCompB_Removed );
    OnAny_Flag<Game,FlagA>.I.Sub( OnFlagA );

    OnSelf<Game,CompB>.I.Sub( entityCreationIndex, OnCompB );
    OnSelf_Removed<Game,CompB>.I.Sub( entityCreationIndex, OnCompB_Removed );
    OnSelf_Flag<Game,FlagA>.I.Sub( entityCreationIndex, OnFlagA );
}

private void OnDestroy()
{
    OnAny<Game,CompB>.I.Unsub( OnCompB );
    OnAny_Removed<Game,CompB>.I.Unsub( OnCompB_Removed );
    OnAny_Flag<Game,FlagA>.I.Unsub( OnFlagA );

    OnSelf<Game,CompB>.I.Unsub( entityCreationIndex, OnCompB );
    OnSelf_Removed<Game,CompB>.I.Unsub( entityCreationIndex, OnCompB_Removed );
    OnSelf_Flag<Game,FlagA>.I.Unsub( entityCreationIndex, OnFlagA );
}

// Events2.I.UnsubAll() will remove all subscriptions across all contexts and components
public static void UnsubscribeAll()
{
    Events2.I.UnsubAll();
}

EntityIndex

// Step 1(Optional). Create const string key for accessing entity index
public static class EntIndex
{
    public const string B = "B";
}


// Step 2. Add EntityIndex during initialization stage
var context = contexts.Get<Game>( );

// for Class Component
context.AddEntityIndex( EntIndex.B
    , context.GetGroup( Matcher<Game, B>.I )
    , ( e, c ) => ( (B)c ).Value );

// for Struct Component
context.AddEntityIndex( EntIndex.S
    , context.GetGroup( Matcher<Game, SStruct>.I )
    , ( e, c ) => ( (StructComponent<SStruct>) c).Data.value );


// Step 3. Get entities at runtime
var entities = context.GetEntities( EntIndex.B, 23 );
// preferred, compile time error checked variation(can be wrapped into extension method for simplicity)
var sameEntities = context.GetAllEntsBy<Game, B, int>( EntIndex.B, 23 );

PrimaryEntityIndex

// Step 1(Optional). Create const string key for accessing entity index
public static class EntIndex
{
    public const string B = "B";
}


// Step 2. Add PrimaryEntityIndex during initialization stage
var context = contexts.Get<Game>( );

// for Class Component
context.AddPrimaryEntityIndex( EntIndex.B
    , context.GetGroup( Matcher<Game, B>.I )
    , ( e, c ) => ( (B)c ).Value );

// for Struct Component
context.AddPrimaryEntityIndex( EntIndex.S
    , context.GetGroup( Matcher<Game, SStruct>.I )
    , ( e, c ) => ( (StructComponent<SStruct>) c).Data.value );


// Step 3. Get entity at runtime
var entity = context.GetEntity( EntIndex.B, 23 );
// preferred, compile time error checked variation(can be wrapped into extension method for simplicity)
var sameEntity = context.GetSingleEntBy<Game, B, int>( EntIndex.B, 23 );

Visual Debugging

public static void InitVisualDebugging ( Contexts contexts )
{
    #if (!ENTITAS_DISABLE_VISUAL_DEBUGGING && UNITY_EDITOR)
    try
    {
        foreach ( var context in contexts.All )
        {
            var observer = new Entitas.VisualDebugging.Unity.ContextObserver(context);
            UnityEngine.Object.DontDestroyOnLoad(observer.gameObject);
        }
    }
    catch(System.Exception)
    {
    }
    #endif
}

Copy somewhere into your project and use var systems = new Feature(); to visually debug Systems.

Because Feature class uses #if preprocessor directives it must be present in unity project in source form and not in a precompiled EntitasGenericAddon dll.

#if (!ENTITAS_DISABLE_VISUAL_DEBUGGING && UNITY_EDITOR)

public class Feature : Entitas.VisualDebugging.Unity.DebugSystems {

    public Feature(string name) : base(name) {
    }

    public Feature() : base(true) {
        var readableType = GetType().ToGenericTypeString();
        initialize(readableType);
    }
}

#elif (!ENTITAS_DISABLE_DEEP_PROFILING && DEVELOPMENT_BUILD)

public class Feature : Entitas.Systems {

    System.Collections.Generic.List<string> _initializeSystemNames;
    System.Collections.Generic.List<string> _executeSystemNames;
    System.Collections.Generic.List<string> _cleanupSystemNames;
    System.Collections.Generic.List<string> _tearDownSystemNames;

    public Feature(string name) : this() {
    }

    public Feature() {
        _initializeSystemNames = new System.Collections.Generic.List<string>();
        _executeSystemNames = new System.Collections.Generic.List<string>();
        _cleanupSystemNames = new System.Collections.Generic.List<string>();
        _tearDownSystemNames = new System.Collections.Generic.List<string>();
    }

    public override Entitas.Systems Add(Entitas.ISystem system) {
        var systemName = system.GetType().ToGenericTypeString();

        if (system is Entitas.IInitializeSystem) {
            _initializeSystemNames.Add(systemName);
        }

        if (system is Entitas.IExecuteSystem) {
            _executeSystemNames.Add(systemName);
        }

        if (system is Entitas.ICleanupSystem) {
            _cleanupSystemNames.Add(systemName);
        }

        if (system is Entitas.ITearDownSystem) {
            _tearDownSystemNames.Add(systemName);
        }

        return base.Add(system);
    }

    public override void Initialize() {
        for (int i = 0; i < _initializeSystems.Count; i++) {
            UnityEngine.Profiling.Profiler.BeginSample(_initializeSystemNames[i]);
            _initializeSystems[i].Initialize();
            UnityEngine.Profiling.Profiler.EndSample();
        }
    }

    public override void Execute() {
        for (int i = 0; i < _executeSystems.Count; i++) {
            UnityEngine.Profiling.Profiler.BeginSample(_executeSystemNames[i]);
            _executeSystems[i].Execute();
            UnityEngine.Profiling.Profiler.EndSample();
        }
    }

    public override void Cleanup() {
        for (int i = 0; i < _cleanupSystems.Count; i++) {
            UnityEngine.Profiling.Profiler.BeginSample(_cleanupSystemNames[i]);
            _cleanupSystems[i].Cleanup();
            UnityEngine.Profiling.Profiler.EndSample();
        }
    }

    public override void TearDown() {
        for (int i = 0; i < _tearDownSystems.Count; i++) {
            UnityEngine.Profiling.Profiler.BeginSample(_tearDownSystemNames[i]);
            _tearDownSystems[i].TearDown();
            UnityEngine.Profiling.Profiler.EndSample();
        }
    }
}

#else

public class Feature : Entitas.Systems {

    public Feature(string name) {
    }

    public Feature() {
    }
}

#endif

FAQ

Q: What Cache<T>.I does?

A: Cache<T>.Icreates and reuses static copy of class component for passing values to Entitas component through manually created Component.Set method. I is shortened Instance. Check CacheT.cs for implementation. There is no need to use Cache<T>.I with struct components.

Q: I have better implementation of some interface/feature

A: Improvements are great! Please write your suggestion in Issues section

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].