[Framework] ACTORS - component framework with simple ECS

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 "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

Documentation

Simple code showcase

using UnityEngine;

namespace Pixeye
{
   public class ProcessingPlayer: ProcessingBase, ITick
   {

       Group<ComponentPlayer, ComponentObject> groupPlayers;
       Group<ComponentPlayer, ComponentWeapon, ComponentObject> groupPlayersWithWeapon;

       public void Tick()
       {
           foreach (var entity in groupPlayers)
           {
               var cPlayer = entity.ComponentPlayer();
               Debug.Log(string.Format("{0} with id {1}", cPlayer.name, entity));
           }

           foreach (var entity in groupPlayersWithWeapon)
           {
               var cPlayer = entity.ComponentPlayer();
               var cWeapon = entity.ComponentWeapon();
               Debug.Log(string.Format("{0} with id {1} holds {2}", cPlayer.name, entity, cWeapon.name));
           }
       }
   }
}
1 Like

Added documentation for game object pooling pattern and timers.

Timers are delayed actions that can be cached.

Timer.Add(0.1f, actor.HandleDestroyGO);

Caching timers and reuse them.

private Timer timerBlink;
timerBlink = new Timer(BlinkFinish, 0.15f);
void Blink(){
timerBlink.Restart();
}

Or add ID to manipulate with group of timers that belong to one object.

Timer.Add(0.1f, actor.HandleDestroyGO).AddID(actor);
var timers = Timer.FindAllTimers(actor);
    
    if (timers != null)
     for (var i = 0; i < timers.Count; i++)
       {
       timers[i].timeScale = 0.5f;
       }

Added descriptions for Blueprints.
Blueprints are scriptable objects for sharing common data among similar objects.

Example of pulling data from blueprint:

var weaponData = Get<DataBlueprint>().Get<DataWeapon>();

Example of blueprint:

[CreateAssetMenu(fileName = "BlueprintCreature", menuName = "Blueprints/BlueprintCreature")]
   public class BlueprintCreature : Blueprint
   {
    [FoldoutGroup("Setup")]
    public DataWeapon dataWeapon;
  
    [FoldoutGroup("Setup")]
    public DataDeathAnimations dataDeathAnimations;

    public override void Setup()
    {
     Add(dataWeapon);
     Add(dataDeathAnimations);
    }
   }

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.

public class ProcessingCameraFollow : ProcessingBase, ITick, IMustBeWipedOut{
[GroupBy(Tag.GroupPlayer)]
[GroupExclude(Tag.StateDead)]
private Group groupPlayers;

   public ProcessingCameraFollow()
   {
      groupPlayers.OnGroupChanged += OnGroupPlayersChanged;
   }

   void OnGroupPlayersChanged()
    { 
      for(var i=0;i<groupPlayers.length;i++){
         Debug.Log("Actor: " + groupPlayers.actors[i]);
      }
    }
   
   public void Tick()
   {
         for(var i=0;i<groupPlayers.length;i++){
         DoSomething(groupPlayers.actors[i]);
            }
    }
   
    void DoSomething(Actor a){
    }
   
}

After finishing documentation I’m going to make a simple game like super crate box to show framework in action : )

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.

// Add stun marker from the mighty hammer of doom.
tags.Add(Tag.Stunned);
// Add stun marker from falling off the tree.
tags.Add(Tag.Stunned);
// remove effect caused by the mighty hammer of doom. 
tags.Remove(Tag.Stunned);
// we are still stunned because we added two stun effects and removed only one
bool condition_stunned = tags.Contain(Tag.Stunned);

Tags can be used in the Inspector window.

public static partial class Tag
    {
  [TagField(categoryName = "Weapons")] public const int WeaponGun = 9000;
  [TagField(categoryName = "Weapons/BigGuns")] public const int WeaponLaser = 9001;
    }
[TagFilter(typeof(Tag))] public int tag;

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.

3543297--284555--ezgif.com-gif-maker.gif

Love all your progress, excellent reading. But what performance hit does your framework have, if any?

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

if (Input.GetKeyDown(KeyCode.Space))
{
   Profile.StartProfile("Framework get method");

   for (int i = 0; i < 100000; i++)
   {
      Get<Rigidbody2D>();
   }


   Profile.EndProfile("Framework get method");

   Profile.StartProfile("Unity get component method");


   for (int i = 0; i < 100000; i++)
   {
      gameObject.GetComponent<Rigidbody2D>();
   }


   Profile.EndProfile("Unity get component method");
   Profile.PrintResults();
}

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 )

1 Like

Hi everyone :slight_smile: 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.

using UnityEngine;
namespace Homebrew
{
    [System.Serializable]
    public class ComponentInputExample : IComponent
    {
        public KeyCode Up;
        public KeyCode Right;
        public KeyCode Down;
        public KeyCode Left;
    }

// I generate methods below from a template in IDE
    public static class ExtensionComponentInputExample
    {
        public static ComponentInputExample ComponentInputExample(this int entity) { return Storage<ComponentInputExample>.Instance.components[entity]; }

        public static bool HasComponentInputExample(this int entity) { return Storage<ComponentInputExample>.Instance.HasComponent(entity); }
    }

}

An actor is a monobehavior component that “link” unity with an abstract entity.

 public class ActorExample : Actor
    {
        [FoldoutGroup("Setup")] public ComponentInputExample componentInputExample;

        protected override void Setup()
        {
            Add(componentInputExample);
        }
    }

Processings are either systems or some “global” game controllers. Usually, you would add them to the toolbox from starter scripts.

using UnityEngine;
namespace Homebrew
{
   public class ProcessingExampleInput : ProcessingBase, ITick
   {
       // A group is a container of all entities that have certain components.
       public Group<ComponentInputExample, ComponentObject> groupInput;

       public void Tick()
       {
           // loop through entities in group
           foreach(var entity in groupInput)
           {
               // get component from group.
               var cInputExample = entity.ComponentInputExample();
               var cObject = entity.ComponentObject();
            
            
               if (Input.GetKeyDown(cInputExample.Up))
                   Debug.Log(cObject.obj + " UP!" );
            
               if (Input.GetKeyDown(cInputExample.Down))
                   Debug.Log(cObject.obj + " DOWN!" );
            
               if (Input.GetKeyDown(cInputExample.Right))
                   Debug.Log(cObject.obj + " RIGHT!" );
            
               if (Input.GetKeyDown(cInputExample.Left))
                   Debug.Log(cObject.obj + " LEFT!" );
           }
       }
   }
}

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();

public class StarterLevel1: Starter
{
   protected override void Setup()
   {
       Add<ProcessingExampleInput>();
   }

}

The game I’m making on a framework
ThankfulInsistentHornshark

Some working code from the project
Processing Motion
Processing Player

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 "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

Documentation

Simple code showcase

using UnityEngine;

namespace Pixeye
{
   public class ProcessingPlayer: ProcessingBase, ITick
   {

       Group<ComponentPlayer, ComponentObject> groupPlayers;
       Group<ComponentPlayer, ComponentWeapon, ComponentObject> groupPlayersWithWeapon;

       public void Tick()
       {
           foreach (var entity in groupPlayers)
           {
               var cPlayer = entity.ComponentPlayer();
               Debug.Log(string.Format("{0} with id {1}", cPlayer.name, entity));
           }

           foreach (var entity in groupPlayersWithWeapon)
           {
               var cPlayer = entity.ComponentPlayer();
               var cWeapon = entity.ComponentWeapon();
               Debug.Log(string.Format("{0} with id {1} holds {2}", cPlayer.name, entity, cWeapon.name));
           }
       }
   }
}
1 Like

Уважаемый, здравствуйте! Я чайник в 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 ‘GitHub - PixeyeHQ/actors.unity: 🚀Actors is a framework empowering developers to make better games faster on Unity.’: Failed to connect to github.com port 443: Bad access
подскажите в чем проблема?

Заранее извиняюсь! Забыл добавить GIT в правила Фаервола :slight_smile: