Search Unity

How do you combine a hybrid ECS with a pure ECS?

Discussion in 'Entity Component System' started by laurentlavigne, Oct 29, 2018.

  1. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,364
    Hybrid makes it easy to move GO, pure is great for heavy lifting so I am trying to adapt influence map AI over to pure.
    The hybrid will move the GOs out of a movement vector calculated by the pure influence map AI pure layer. How do I feed the pure with hybrid data, and back to hybrid and how do I limit the refresh rate of the pure ECS layer?
    Cheers.
     
    Last edited: Oct 29, 2018
    SugoiDev likes this.
  2. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    Call systems update with frequency which you need. Not understand your question, what you mean in "combine"?
     
  3. Derebeyi

    Derebeyi

    Joined:
    Jun 3, 2015
    Posts:
    7
    There is no way to limit ECS frequency but you can make your systems wait. You can have a frame counter which incremented every update.Systems act according to that.Like every X frame this system wil run.

    Below example shows system called "LimitedSystem" running every frame but only doing work every second (assuming you hit 60 FPS) at 30th frame.

    Code (CSharp):
    1. public struct FrameCounterData:IComponentData
    2. {
    3.     public int frameCounter;
    4. }
    5.  
    6. public class FrameSystem:ComponentSystem
    7. {
    8.     public struct Data
    9.     {
    10.         public readonly int Length;
    11.         public ComponentDataArray<FrameCounterData> data1;
    12.     }
    13.  
    14. [Inject] private Data data;
    15.  
    16.     protected override void OnUpdate()
    17.     {
    18.         //You don't need a loop if there is only one FrameCounterData on world.
    19.         for (int i = 0; i < data.Length; i++)
    20.         {
    21.             FrameCounterData frameData=data.data1[0];
    22.             frameData.currentFrame++;
    23.  
    24.             if(currentFrame >= 60)
    25.             {
    26.                 frameData.currentFrame=0;
    27.             }
    28.            
    29.             data.data1[i]=frameData;
    30.         }
    31.    
    32.     }
    33. }
    34.  
    35. public class LimitedSystem:ComponentSystem //The system you want to limit/wait.
    36. {
    37.     public struct Data
    38.     {
    39.         public readonly int Length;
    40.         public ComponentDataArray<FrameCounterData> data1;
    41.     }
    42.  
    43. [Inject] private Data data;
    44.  
    45.     protected override void OnUpdate()
    46.     {
    47.         if(data.data1[0].frameCounter == 30)
    48.             //do stuff  
    49.     }
    50.  
    51. }
    52.  
    53.  

    Transfering data between GO and ECS worlds are cumbersome at this point.ComponentSystem and MonoBehaviour can access to EntityManager.With EntityManager you can pull components and thus their data.

    Let's say you wish to show your frameCounter on console.

    Code (CSharp):
    1.         var v = manager.GetComponentDataFromEntity<FrameCounterData>();
    2.         Debug.Log(v[e].frameCounter.ToString()); //"e" is entity
    This does that and it can be called from MonoBehaviours.Problem is you need to know Entity to pass as parameter.There is EntityManager.GetAllEntities() methode,but it gives all entities and looping through them is not performant.Another option is chunk iteration.But I can't give an example at this time.Feeding databack to ECS can be done in same whay.You can use EntityManager.SetComponentData() to pass data to components then your systems can process them.

    These are simple examples,I hope someone who knows better than me shows a better approach.
     
    laurentlavigne likes this.
  4. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,364
    Can't find the API for that, how do you do that?
    I think there is a way to fill a buffer that crosses the pure/main thread barrier.
     
  5. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    I mean manage them manually. All managers has .Update(). Create systems manually, ang manage them manually. (for separating this from default world create your own, and update systems when you need.
    Code (CSharp):
    1.         foreach (var manager in myWorld.BehaviourManagers)
    2.         {
    3.             manager.Update();
    4.         }
     
    laurentlavigne likes this.
  6. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,364
    How do I create a world and assign an entitymanager to it? the doc links on world are all dead
     
  7. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    In very very basic implementation:

    Bootstrap (can be system in other world, not only MonoBehaviour, is just for fast sample, update "Eizenhorn World" every 100 frames)
    Code (CSharp):
    1. using Unity.Entities;
    2. using UnityEngine;
    3.  
    4. public class ManualWorldBootstrap : MonoBehaviour
    5. {
    6.     private World _manualWorld;
    7.     private float _udateCounter;
    8.  
    9.     void Start()
    10.     {
    11.         _manualWorld = new World("Eizenhorn World");
    12.         _manualWorld.CreateManager<ManualWorldSystem>().Enabled = false;
    13.         ScriptBehaviourUpdateOrder.UpdatePlayerLoop(_manualWorld);
    14.     }
    15.  
    16.     void Update()
    17.     {
    18.         Debug.Log("Frame tick");
    19.         _udateCounter++;
    20.         if (_udateCounter == 100)
    21.         {
    22.             _udateCounter = 0;
    23.             foreach (var manager in _manualWorld.BehaviourManagers)
    24.             {
    25.                 _manualWorld.GetExistingManager<ManualWorldSystem>().Enabled = true;
    26.                 manager.Update();
    27.                 _manualWorld.GetExistingManager<ManualWorldSystem>().Enabled = false;
    28.             }
    29.         }
    30.     }
    31. }
    System (every update creates new entity)
    Code (CSharp):
    1. using Unity.Entities;
    2. using Unity.Transforms;
    3. using UnityEngine;
    4.  
    5. public struct ManualWorldComponent : IComponentData {}
    6.  
    7. [DisableAutoCreation, AlwaysUpdateSystem]
    8. public class ManualWorldSystem : ComponentSystem
    9. {
    10.     private ComponentGroup _manualWorldGroup;
    11.     protected override void OnCreateManager()
    12.     {
    13.         base.OnCreateManager();
    14.         _manualWorldGroup = GetComponentGroup(typeof(Position), typeof(Rotation), typeof(ManualWorldComponent));
    15.         Debug.Log("Manager created");
    16.     }
    17.  
    18.     protected override void OnUpdate()
    19.     {
    20.         EntityManager.CreateEntity(typeof(Position), typeof(Rotation), typeof(ManualWorldComponent));
    21.         Debug.Log("<color=green>Manager updated - </color> items in group " + _manualWorldGroup.CalculateLength());
    22.     }
    23. }
    Result
    upload_2018-11-1_11-9-46.png
     
    laurentlavigne and e199 like this.
  8. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,364
    Thanks! Perfect documentation example :)
    Interesting bit of automagic: as soon as UpdatePlayerLoop is set, all other systems that relied on autoboostrapping stop running. In order to keep them running, World.Active needs to be added to the parameters.
     
  9. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    1) you don't need to update player loop if you are updating the world manually. it just interfere with the other worlds.
    2) you are setting
    _manualWorld.GetExistingManager<ManualWorldSystem>().Enabled
    in the loop. did you mean to set
    manager.Enabled
    ?

    if you instead wanted to only manually update one system, do
    ScriptBehaviourUpdateOrder.UpdatePlayerLoop(World.AllWorlds);
    and get rid of the loop (also cache your system instance)
     
  10. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    1) Yep is not needed, just wrote this by inertion as in default wold manager initialisation :)

    2) After creating I disable system for preventing OnOpdate calls in system (because system marked AlwaysUpdateSystem, otherwise if you not mark system AlwaysUpdateSystem and in world not exists any entity which can be handled by this system (in ComponentGroup for example), Update on manager not do any things, thus system never run, until entity which can be handled not be created in world, otherwise if entity exists system start runs OnUpdate every frame), and then iterate through all managers in world enable them, update and disable, thus system updates ONLY when I call it, without any early out or empty updates.

    ScriptBehaviourUpdateOrder.UpdatePlayerLoop(World.AllWorlds) will not work :) You must convert it to Array of World becausle World.AllWorlds is ReadOnlyCollection<T> :)
    ScriptBehaviourUpdateOrder.UpdatePlayerLoop(World.AllWorlds.ToArray()) is correct.
    You not need cache managers, because internally is Dictionary, and just getting manager by Type as key is O1, cache in this case - overhead.
    upload_2018-11-2_15-33-53.png
     
    Last edited: Nov 2, 2018
  11. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    2) you are continually enabaling/disabling the same system in the loop, when calling Update on the other systems.
    you either want to disable/manually update one system (in that case you cache it when you create&disable it in Start, then you do not loop all the systems each frame) or the entire world (in that case you need to enable/disable the system you are iterating, not the same system over and over)

    ps. dictionary lookup is still a lookup (plus arg checking), and
    _manualSystem
    is less typing than
    _manualWorld.GetExistingManager<ManualWorldSystem>()
     
  12. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    Oh I understand which you mean, you right, absolutely looked through this moment thx, of course I mean Enable\Disable current system in loop :) And without player loop is not needed

    O1 that says it all, I repeat, this is an overhead.
     
    Last edited: Nov 2, 2018
  13. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    Here fixed variation, thanks @M_R for pointing to some incorrect places :)

    Code (CSharp):
    1. using Unity.Entities;
    2. using UnityEngine;
    3.  
    4. public class ManualWorldBootstrap : MonoBehaviour
    5. {
    6.     private World _manualWorld;
    7.     private float _udateCounter;
    8.  
    9.     void Start()
    10.     {
    11.         _manualWorld                                            = new World("Eizenhorn World");
    12.         _manualWorld.CreateManager<ManualWorldSystem>();
    13.     }
    14.  
    15.  
    16.     void Update()
    17.     {
    18.         Debug.Log("Frame tick");
    19.         _udateCounter++;
    20.         if (_udateCounter == 100)
    21.         {
    22.             _udateCounter = 0;
    23.             foreach (var manager in _manualWorld.BehaviourManagers)
    24.             {
    25.                 manager.Update();
    26.             }
    27.         }
    28.     }
    29. }
    Code (CSharp):
    1. using Unity.Entities;
    2. using Unity.Transforms;
    3. using UnityEngine;
    4.  
    5. public struct ManualWorldComponent : IComponentData {}
    6.  
    7. [DisableAutoCreation, AlwaysUpdateSystem]
    8. public class ManualWorldSystem : ComponentSystem
    9. {
    10.     private ComponentGroup _manualWorldGroup;
    11.     protected override void OnCreateManager()
    12.     {
    13.         base.OnCreateManager();
    14.         _manualWorldGroup = GetComponentGroup(typeof(Position), typeof(Rotation), typeof(ManualWorldComponent));
    15.         Debug.Log("Manager created");
    16.     }
    17.  
    18.     public void UpdateManually()
    19.     {
    20.         OnUpdate();
    21.     }
    22.  
    23.     protected override void OnUpdate()
    24.     {
    25.         EntityManager.CreateEntity(typeof(Position), typeof(Rotation), typeof(ManualWorldComponent));
    26.         Debug.Log("<color=green>Manager updated - </color> items in group " + _manualWorldGroup.CalculateLength());
    27.     }
    28. }
     
    Last edited: Nov 2, 2018
    Vacummus likes this.
  14. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,364
    Code?
     
  15. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    Your update code seems a bit odd. Why not do it in a frame-rate independent way instead, using either delta time, cooroutines or maybe even the InvokeRepeating function?

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3.  
    4. public class ManualWorldBootstrap : MonoBehaviour
    5. {
    6.     private World _manualWorld;
    7.     private float _udateCounter;
    8.  
    9.     void Start ()
    10.     {
    11.         _manualWorld = new World("Eizenhorn World");
    12.         _manualWorld.CreateManager<ManualWorldSystem> ();
    13.         InvokeRepeating("UpdateWorld", 1, 1);
    14.     }
    15.  
    16.     void UpdateWorld ()
    17.     {
    18.         foreach (var manager in _manualWorld.BehaviourManagers)
    19.         {
    20.             manager.Update ();
    21.         }
    22.  
    23.         Debug.Log ("World updated!");
    24.     }
    25. }
    26.  
     
    Last edited: Nov 6, 2018
  16. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    1. Invoke is reflection, and garbage, and under hood it’s do same thing.
    2. Coroutines - too, generate garbage.
    Simple timer use only stack and not generate garbage. Also using in monobehaviour is just sample, you can do this in other ComponentSystem/JobComponentSystem in other World, where you not inherit monobehaviour and don't have invoke or coroutines.
     
    Whippets likes this.
  17. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    All true but my main point was that your sample code is not frame-rate independent and I was simply giving multiple alternatives. Garbage collection is also not really an issue for such a simple function/example, and if it is you probably woudn't use a Monobehaviour to begin with. You also completely ignored the delta time suggestion, which is what I would use, but InvokeRepeating is simplest so it works great as a sample.
     
  18. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    And Invoke it’s ugly like SendMessage, It’s bad practice
     
    Last edited: Nov 4, 2018
  19. IsaiahKelly

    IsaiahKelly

    Joined:
    Nov 11, 2012
    Posts:
    418
    Now return to first quote. WARNING: Infinite loop detected! :eek:

    We seem to both agree it's just a sample and doesn't really matter. So... maybe we can just forget about this whole thing? I'm sorry to derail the tread. I should have never made the original comment.
     
  20. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    Oh of course, may be I incorrect understand your agreement, laguage barrier :) Anyway, no problem, forget about our discussion :)
     
    IsaiahKelly likes this.
  21. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,364
    This updates every frames and with ScriptBehaviourUpdateOrder.UpdatePlayerLoop(_manualWorld); it doesn't update at all
    Code (CSharp):
    1.     using Unity.Entities;
    2.     using UnityEngine;
    3.    
    4.     public class WobblerWorldBootstrap : MonoBehaviour
    5.     {
    6.         private World _wobbleWorld;
    7.         private float _udateInterval;
    8.    
    9.         void Start()
    10.         {
    11.             _wobbleWorld = new World("Wobble World");
    12.             _wobbleWorld.CreateManager<WobblerSystem>().Enabled = false;
    13.             ScriptBehaviourUpdateOrder.UpdatePlayerLoop(_wobbleWorld);
    14.         }
    15.    
    16.         void Update()
    17.         {
    18.             if (Time.time - _udateInterval >1.0f)
    19.             {
    20.                 _udateInterval = Time.time;
    21.                 foreach (var manager in _wobbleWorld.BehaviourManagers)
    22.                     manager.Update();
    23.             }
    24.         }
    25.     }
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Unity.Entities;
    5. using Unity.Transforms;
    6.  
    7. public class WobblerSystem : ComponentSystem
    8. {
    9.     struct Group
    10.     {
    11.         public Transform transform;
    12.         public WobblerComponent wobble;
    13.     }
    14.    
    15.     protected override void OnUpdate()
    16.     {
    17.         Debug.Log("<color=green>Wobbler Manager updated - </color> entities in group " + GetEntities<Group>().Length);
    18.         foreach (var e in GetEntities<Group>())
    19.         {
    20.             e.transform.position = e.wobble.initial + new Vector2(
    21.                                     e.wobble.amplitude.x * Mathf.Sin(Time.time*e.wobble.speed.x + e.wobble.offset.x),
    22.                                     e.wobble.amplitude.y * Mathf.Cos(Time.time*e.wobble.speed.y + e.wobble.offset.y));
    23.  
    24.         }
    25.     }
    26. }
    27.  
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class WobblerComponent : MonoBehaviour
    6. {
    7.     public Vector2 amplitude= new Vector2(1.0f,2.0f);
    8.     internal Vector2 initial,offset,speed;
    9.    
    10.     void OnEnable()
    11.     {
    12.         initial = transform.position;
    13.         offset = Random.insideUnitCircle;
    14.         speed = Random.insideUnitCircle * 3;
    15.     }
    16. }
    17.  
     
  22. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,364
    How do you declare manager.?
     
  23. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    Nope it works as I show on screenshot and call system update only when we do that. As I see you not pass attributes [DisableAutoCreation, AlwaysUpdateSystem] thus your system creates in default world on initialization too.
     
  24. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,364
    I added those attributes, now it doesn't update at all.
    What do AlwaysUpdateSystem mean in the context of a world that's updated by hand?
     
  25. thelebaron

    thelebaron

    Joined:
    Jun 2, 2013
    Posts:
    857
    is a system that has [DisableAutoCreation] supposed to show up in the entity debugger? Because Ive found in my limited testing it doesnt appear to show up(but it does seem to work)
     
  26. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    You doing something wrong, I show that code forks fine. Copy paste here your code.
    I told about [AlwaysUpdateSystem] above. Read thread :)