Search Unity

[Framework] ACTORS - component framework with simple ECS

Discussion in 'Made With Unity' started by Pixeye, May 29, 2018.

  1. Pixeye

    Pixeye

    Joined:
    Jul 2, 2011
    Posts:
    193



    - Discord
    - Twitter
    - Github

    ACTORS is a complete game framework with multiscene editing, game object pooling and ECS ( entity-component-system ) data-driven approach for game logic explicitly built for Unity3d. It is used to ease the pain of decoupling data from behaviors without tons of boilerplate code and without unnecessary overhead.

    Features

    - ECS events ( can be extended with Unirx )
    - Very lightweight ECS syntax
    - Actors ( visual entity composer in the Unity Inspector window )
    - Built-in support for pooling
    - Built-in support for Unity multiscene editing
    - Built-in support for plugins with a pluggable wrapper that you can share with others/through projects
    - Built-in support for updates through ITick, ITickFixed, ITickLate - ( faster than Unity Update methods )
    - Signals ( in-memory publish/subscribe system and effectively replace Unity3d SendMessage )
    - Tags ( add simple tags to entities to define states )
    - Editor extensions ( foldout group in the inspector and tags editing )
    - Numerous monobehavior components to help interact with the framework
    - Templates for creating scriptable objects
    - Game console plugin for commands and cheats
    - Framework can be extended with ODIN inspector

    Requirements

    - Unity 2018 and higher

    How to Install

    From packages

    - Create a new Unity Project
    - Open the manifest.json file in the Packages folder inside of the Project
    - Add
    Code (csharp):
    1. "com.pixeye.ecs": "https://github.com/dimmpixeye/ecs.unity.git",
    - Press Tools->Actors->Update Framework[GIT] in Unity to get new update when needed

    From Unity

    - Download from https://github.com/dimmpixeye/ecs/releases

    Documentation

    * Examples
    * Wiki

    Simple code showcase


    Code (csharp):
    1. using UnityEngine;
    2.  
    3. namespace Pixeye
    4. {
    5.    public class ProcessingPlayer: ProcessingBase, ITick
    6.    {
    7.  
    8.        Group<ComponentPlayer, ComponentObject> groupPlayers;
    9.        Group<ComponentPlayer, ComponentWeapon, ComponentObject> groupPlayersWithWeapon;
    10.  
    11.        public void Tick()
    12.        {
    13.            foreach (var entity in groupPlayers)
    14.            {
    15.                var cPlayer = entity.ComponentPlayer();
    16.                Debug.Log(string.Format("{0} with id {1}", cPlayer.name, entity));
    17.            }
    18.  
    19.            foreach (var entity in groupPlayersWithWeapon)
    20.            {
    21.                var cPlayer = entity.ComponentPlayer();
    22.                var cWeapon = entity.ComponentWeapon();
    23.                Debug.Log(string.Format("{0} with id {1} holds {2}", cPlayer.name, entity, cWeapon.name));
    24.            }
    25.        }
    26.    }
    27. }
     
    Last edited: Feb 27, 2019
    viciadotm likes this.
  2. Pixeye

    Pixeye

    Joined:
    Jul 2, 2011
    Posts:
    193
    Added documentation for game object pooling pattern and timers.

    Timers are delayed actions that can be cached.
    Code (CSharp):
    1. Timer.Add(0.1f, actor.HandleDestroyGO);
    Caching timers and reuse them.

    Code (CSharp):
    1. private Timer timerBlink;
    2. timerBlink = new Timer(BlinkFinish, 0.15f);
    3. void Blink(){
    4. timerBlink.Restart();
    5. }
    Or add ID to manipulate with group of timers that belong to one object.

    Code (CSharp):
    1. Timer.Add(0.1f, actor.HandleDestroyGO).AddID(actor);
    2. var timers = Timer.FindAllTimers(actor);
    3.    
    4.     if (timers != null)
    5.      for (var i = 0; i < timers.Count; i++)
    6.        {
    7.        timers[i].timeScale = 0.5f;
    8.        }
     
  3. Pixeye

    Pixeye

    Joined:
    Jul 2, 2011
    Posts:
    193
    Added descriptions for Blueprints.
    Blueprints are scriptable objects for sharing common data among similar objects.

    Example of pulling data from blueprint:
    Code (csharp):
    1. var weaponData = Get<DataBlueprint>().Get<DataWeapon>();
    Example of blueprint:
    Code (csharp):
    1. [CreateAssetMenu(fileName = "BlueprintCreature", menuName = "Blueprints/BlueprintCreature")]
    2.    public class BlueprintCreature : Blueprint
    3.    {
    4.     [FoldoutGroup("Setup")]
    5.     public DataWeapon dataWeapon;
    6.  
    7.     [FoldoutGroup("Setup")]
    8.     public DataDeathAnimations dataDeathAnimations;
    9.  
    10.     public override void Setup()
    11.     {
    12.      Add(dataWeapon);
    13.      Add(dataDeathAnimations);
    14.     }
    15.    }
     
  4. Pixeye

    Pixeye

    Joined:
    Jul 2, 2011
    Posts:
    193
    Added documentation for ECS approach.
    Simple ECS pattern for working with actors. My approach can be used only with actor classes at the current moment and is far less powerful than clean ECS approaches and it's used more for structuring than gaining performance boost.


    Code (csharp):
    1.  
    2. public class ProcessingCameraFollow : ProcessingBase, ITick, IMustBeWipedOut{
    3. [GroupBy(Tag.GroupPlayer)]
    4. [GroupExclude(Tag.StateDead)]
    5. private Group groupPlayers;
    6.  
    7.    public ProcessingCameraFollow()
    8.    {
    9.       groupPlayers.OnGroupChanged += OnGroupPlayersChanged;
    10.    }
    11.  
    12.    void OnGroupPlayersChanged()
    13.     {
    14.       for(var i=0;i<groupPlayers.length;i++){
    15.          Debug.Log("Actor: " + groupPlayers.actors[i]);
    16.       }
    17.     }
    18.    
    19.    public void Tick()
    20.    {
    21.          for(var i=0;i<groupPlayers.length;i++){
    22.          DoSomething(groupPlayers.actors[i]);
    23.             }
    24.     }
    25.    
    26.     void DoSomething(Actor a){
    27.     }
    28.    
    29. }
    30.  
     
    Last edited: Jun 1, 2018
  5. Pixeye

    Pixeye

    Joined:
    Jul 2, 2011
    Posts:
    193
    After finishing documentation I'm going to make a simple game like super crate box to show framework in action : )

     
  6. Pixeye

    Pixeye

    Joined:
    Jul 2, 2011
    Posts:
    193
    Added descriptions for tags
    Tags are the glue for your game: You can identify your actors with tags or use them as arguments for your signals to check game logic. Tags are simple cont INT variables.


    Code (csharp):
    1. // Add stun marker from the mighty hammer of doom.
    2. tags.Add(Tag.Stunned);
    3. // Add stun marker from falling off the tree.
    4. tags.Add(Tag.Stunned);
    5. // remove effect caused by the mighty hammer of doom.
    6. tags.Remove(Tag.Stunned);
    7. // we are still stunned because we added two stun effects and removed only one
    8. bool condition_stunned = tags.Contain(Tag.Stunned);  
    Tags can be used in the Inspector window.

    Code (csharp):
    1. public static partial class Tag
    2.     {
    3.   [TagField(categoryName = "Weapons")] public const int WeaponGun = 9000;
    4.   [TagField(categoryName = "Weapons/BigGuns")] public const int WeaponLaser = 9001;
    5.     }
    6.  

    Code (csharp):
    1. [TagFilter(typeof(Tag))] public int tag;
    2.  

     
  7. Pixeye

    Pixeye

    Joined:
    Jul 2, 2011
    Posts:
    193
    A small but important update. Now after saving a new scene, it will be automatically added to project build and generate unique data_yourscenename asset that you need to put inside of your starters. Also, I've added special enum helper to ease scenes navigation via code.



    + small project to show concept in action.

    ezgif.com-gif-maker.gif
     
    Last edited: Jun 25, 2018
  8. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    25,453
    Love all your progress, excellent reading. But what performance hit does your framework have, if any?
     
  9. Pixeye

    Pixeye

    Joined:
    Jul 2, 2011
    Posts:
    193
    I never did any huge measurings but some stress test from games I develop :

    https://i.gyazo.com/9584d2e831aa3f00a8f8c2fc4898d6cb.mp4
    https://i.gyazo.com/f5890ff74da46297f32ba460f6796fc0.mp4


    Comparing to unity for example I have ultra fast get component calls.




    Profile Framework get method took 0.057234000 seconds to complete over 20 iterations, averaging 0.002861700 seconds per call

    Profile Unity get component method took 0.113563100 seconds to complete over 20 iterations, averaging 0.005678155 seconds per call



    Code (CSharp):
    1. if (Input.GetKeyDown(KeyCode.Space))
    2. {
    3.    Profile.StartProfile("Framework get method");
    4.  
    5.    for (int i = 0; i < 100000; i++)
    6.    {
    7.       Get<Rigidbody2D>();
    8.    }
    9.  
    10.  
    11.    Profile.EndProfile("Framework get method");
    12.  
    13.    Profile.StartProfile("Unity get component method");
    14.  
    15.  
    16.    for (int i = 0; i < 100000; i++)
    17.    {
    18.       gameObject.GetComponent<Rigidbody2D>();
    19.    }
    20.  
    21.  
    22.    Profile.EndProfile("Unity get component method");
    23.    Profile.PrintResults();
    24. }
    All my game runs from single update method. I run some tests making about 10 000 unity objects with updates vs actors with ticks and it was about ~30% performance boost. ( it was about year ago so maybe unity devs made Update method better than it was )
     
    hippocoder likes this.
  10. Pixeye

    Pixeye

    Joined:
    Jul 2, 2011
    Posts:
    193
    Hi everyone :) It's been a while since the last news about the framework, but it continues to evolve. I love ECS paradigm and happy to share my vision on the programming workflow.

    Component holds variables.
    Code (CSharp):
    1. using UnityEngine;
    2. namespace Homebrew
    3. {
    4.     [System.Serializable]
    5.     public class ComponentInputExample : IComponent
    6.     {
    7.         public KeyCode Up;
    8.         public KeyCode Right;
    9.         public KeyCode Down;
    10.         public KeyCode Left;
    11.     }
    12.  
    13. // I generate methods below from a template in IDE
    14.     public static class ExtensionComponentInputExample
    15.     {
    16.         public static ComponentInputExample ComponentInputExample(this int entity) { return Storage<ComponentInputExample>.Instance.components[entity]; }
    17.  
    18.         public static bool HasComponentInputExample(this int entity) { return Storage<ComponentInputExample>.Instance.HasComponent(entity); }
    19.     }
    20.  
    21. }
    An actor is a monobehavior component that "link" unity with an abstract entity.
    Code (CSharp):
    1.  public class ActorExample : Actor
    2.     {
    3.         [FoldoutGroup("Setup")] public ComponentInputExample componentInputExample;
    4.  
    5.         protected override void Setup()
    6.         {
    7.             Add(componentInputExample);
    8.         }
    9.     }
    Processings are either systems or some "global" game controllers. Usually, you would add them to the toolbox from starter scripts.
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. namespace Homebrew
    4. {
    5.    public class ProcessingExampleInput : ProcessingBase, ITick
    6.    {
    7.        // A group is a container of all entities that have certain components.
    8.        public Group<ComponentInputExample, ComponentObject> groupInput;
    9.  
    10.        public void Tick()
    11.        {
    12.            // loop through entities in group
    13.            foreach(var entity in groupInput)
    14.            {
    15.                // get component from group.
    16.                var cInputExample = entity.ComponentInputExample();
    17.                var cObject = entity.ComponentObject();
    18.            
    19.            
    20.                if (Input.GetKeyDown(cInputExample.Up))
    21.                    Debug.Log(cObject.obj + " UP!" );
    22.            
    23.                if (Input.GetKeyDown(cInputExample.Down))
    24.                    Debug.Log(cObject.obj + " DOWN!" );
    25.            
    26.                if (Input.GetKeyDown(cInputExample.Right))
    27.                    Debug.Log(cObject.obj + " RIGHT!" );
    28.            
    29.                if (Input.GetKeyDown(cInputExample.Left))
    30.                    Debug.Log(cObject.obj + " LEFT!" );
    31.            }
    32.        }
    33.    }
    34. }
    Starters are classes that initialize scene and can be used to add all processing you would need on the level. Developers control what scenes to load from starter scripts. Using add method in starter class is equal to Toolbox.Add();
    Code (csharp):
    1.  
    2. public class StarterLevel1: Starter
    3. {
    4.    protected override void Setup()
    5.    {
    6.        Add<ProcessingExampleInput>();
    7.    }
    8.  
    9. }
     
  11. Pixeye

    Pixeye

    Joined:
    Jul 2, 2011
    Posts:
    193
  12. Pixeye

    Pixeye

    Joined:
    Jul 2, 2011
    Posts:
    193
    It's been a while since my last post : ) So I never stopped evolving the framework and a new version is ready:

    Features

    - ECS events ( can be extended with Unirx )
    - Very lightweight ECS syntax
    - Actors ( visual entity composer in the Unity Inspector window )
    - Built-in support for pooling
    - Built-in support for Unity multiscene editing
    - Built-in support for plugins with a pluggable wrapper that you can share with others/through projects
    - Built-in support for updates through ITick, ITickFixed, ITickLate - ( faster than Unity Update methods )
    - Signals ( in-memory publish/subscribe system and effectively replace Unity3d SendMessage )
    - Tags ( add simple tags to entities to define states )
    - Editor extensions ( foldout group in the inspector and tags editing )
    - Numerous monobehavior components to help interact with the framework
    - Templates for creating scriptable objects
    - Game console plugin for commands and cheats
    - Framework can be extended with ODIN inspector

    Requirements

    - Unity 2018 and higher

    How to Install

    From packages

    - Create a new Unity Project
    - Open the manifest.json file in the Packages folder inside of the Project
    - Add
    Code (csharp):
    1. "com.pixeye.ecs": "https://github.com/dimmpixeye/ecs.unity.git",
    - Press Tools->Actors->Update Framework[GIT] in Unity to get new update when needed

    From Unity

    - Download from https://github.com/dimmpixeye/ecs/releases

    Documentation

    * Examples
    * Wiki

    Simple code showcase


    Code (csharp):
    1. using UnityEngine;
    2.  
    3. namespace Pixeye
    4. {
    5.    public class ProcessingPlayer: ProcessingBase, ITick
    6.    {
    7.  
    8.        Group<ComponentPlayer, ComponentObject> groupPlayers;
    9.        Group<ComponentPlayer, ComponentWeapon, ComponentObject> groupPlayersWithWeapon;
    10.  
    11.        public void Tick()
    12.        {
    13.            foreach (var entity in groupPlayers)
    14.            {
    15.                var cPlayer = entity.ComponentPlayer();
    16.                Debug.Log(string.Format("{0} with id {1}", cPlayer.name, entity));
    17.            }
    18.  
    19.            foreach (var entity in groupPlayersWithWeapon)
    20.            {
    21.                var cPlayer = entity.ComponentPlayer();
    22.                var cWeapon = entity.ComponentWeapon();
    23.                Debug.Log(string.Format("{0} with id {1} holds {2}", cPlayer.name, entity, cWeapon.name));
    24.            }
    25.        }
    26.    }
    27. }
     
    e199 likes this.
  13. Vazovsky

    Vazovsky

    Joined:
    May 14, 2019
    Posts:
    2
    Уважаемый, здравствуйте! Я чайник в Unity, у меня версия 2019.1.2, пытаюсь добавить ваш фреймворк, вкладку Tools в упор не нашел. Открывал ваш демо проект CryoshockMini-master, в манифесте уже прописан адрес GIT репозитория, Unity мне сообщает о неверном запросе.
    An error occurred while resolving packages:
    Project has invalid dependencies:
    com.pixeye.ecs: Error when executing git command. fatal: unable to access 'https://github.com/dimmpixeye/ecs.git/': Failed to connect to github.com port 443: Bad access
    подскажите в чем проблема?
     
  14. Vazovsky

    Vazovsky

    Joined:
    May 14, 2019
    Posts:
    2
    Заранее извиняюсь! Забыл добавить GIT в правила Фаервола :)