Search Unity

React on multiple event entities to change one entity

Discussion in 'Entity Component System' started by e199, Dec 1, 2018.

  1. e199

    e199

    Joined:
    Mar 24, 2015
    Posts:
    101
    Hey

    I have a system which reacts on physics events and changes components on corresponding entities.
    But there is a problem when you need to process two events targeting single entity.

    First problem

    Everything was working great when I had only one possible event of each type per frame, I just attached that component to entity and it was easy to query.

    Here is emission part:
    Code (CSharp):
    1. using System.Collections.Generic;
    2. using Unity.Entities;
    3. using UnityEngine;
    4.  
    5. public class PhysicsEmitter : MonoBehaviour
    6. {
    7.     private static readonly Dictionary<Collider, GameObjectEntity> Cache = new Dictionary<Collider, GameObjectEntity>(256);
    8.  
    9.     public GameObjectEntity Entity;
    10.     private PhysicsEventEmissionBarrier _barrier;
    11.  
    12.     private EntityCommandBuffer Buffer
    13.     {
    14.         get { return _barrier.CreateCommandBuffer(); }
    15.     }
    16.  
    17.     private void Start()
    18.     {
    19.         _barrier = World.Active.GetOrCreateManager<PhysicsEventEmissionBarrier>();
    20.     }
    21.  
    22.     private GameObjectEntity GetEntity(Collider other)
    23.     {
    24.         GameObjectEntity e;
    25.         var found = Cache.TryGetValue(other, out e);
    26.    
    27.         if (!found)
    28.         {
    29.             e = other.GetComponent<GameObjectEntity>();
    30.             Cache.Add(other, e);
    31.         }
    32.  
    33.         return e;
    34.     }
    35.  
    36.     private void OnTriggerEnter(Collider other)
    37.     {
    38.         var e = GetEntity(other);
    39.         if (e != null)
    40.         {
    41.             Buffer.CreateEntity(PhysicsBridge.TriggerEnterArchetype);
    42.             Buffer.SetComponent(new TriggerEnter {Emitter = Entity.Entity, Target = e.Entity});
    43.         }
    44.     }
    45.  
    46.     private void OnTriggerExit(Collider other)
    47.     {
    48.         var e = GetEntity(other);
    49.         if (e != null)
    50.         {
    51.             Buffer.CreateEntity(PhysicsBridge.TriggerExitArchetype);
    52.             Buffer.SetComponent(new TriggerExit {Emitter = Entity.Entity, Target = e.Entity});
    53.         }
    54.     }
    55. }
    56.  
    57. public struct TriggerEnter : IComponentData
    58. {
    59.     public Entity Emitter;
    60.     public Entity Target;
    61. }
    62.  
    63. public struct TriggerExit : IComponentData
    64. {
    65.     public Entity Emitter;
    66.     public Entity Target;
    67. }

    And actual system which consumes events:
    Code (CSharp):
    1. using Unity.Collections;
    2. using Unity.Entities;
    3. using UnityEngine;
    4.  
    5. [UpdateAfter(typeof(PhysicsEventEmissionBarrier))]
    6. [UpdateBefore(typeof(EventRemoveBarrier))]
    7. public class AddHeatingSystem : ComponentSystem
    8. {
    9.     protected struct Data
    10.     {
    11.         public readonly int Length;
    12.  
    13.         public ComponentDataArray<TriggerEnter> Event;
    14.     }
    15.  
    16.     [Inject] protected Data Events;
    17.  
    18.     [ReadOnly] [Inject] public ComponentDataFromEntity<Heating>  HeatingEntities;
    19.     [ReadOnly] [Inject] public ComponentDataFromEntity<Flamable> FlamableEntities;
    20.     [ReadOnly] [Inject] public ComponentDataFromEntity<Flamed>   FlamedEntities;
    21.  
    22.     protected override void OnUpdate()
    23.     {
    24.         if (Events.Length > 0)
    25.         {
    26.             NativeList<Entity> addedEntities = new NativeList<Entity>(16, Allocator.Temp);
    27.             for (int i = 0; i < Events.Length; i++)
    28.             {
    29.                 var eventComponent = Events.Event[i];
    30.                 var emitter = eventComponent.Emitter;
    31.                 var target = eventComponent.Target;
    32.                 if (FlamedEntities.Exists(target) && FlamableEntities.Exists(emitter))
    33.                 {
    34.                     if (HeatingEntities.Exists(emitter) || addedEntities.Contains(emitter))
    35.                     {
    36.                         Debug.Log("HEAT INCREASED");
    37.                         var heat = EntityManager.GetComponentData<Heating>(emitter);
    38.                         heat.Count++;
    39.                         EntityManager.SetComponentData(emitter, heat);
    40.                     }
    41.                     else
    42.                     {
    43.                         Debug.Log("HEAT ADDED");
    44.                         var heat = new Heating{Count = 1};
    45.                         EntityManager.AddComponentData(emitter, heat);
    46.                         addedEntities.Add(emitter);
    47.                     }
    48.                 }
    49.             }
    50.             addedEntities.Dispose();
    51.         }
    52.     }
    53. }
    As you can see I tried to be "clever" and store entities which got new component.

    The problem is EntityManager invalidates all injects...
    And I can't use my old approach with CommandBuffers, except if I store everything I changed in my own arrays.

    How would you solve it?

    How can you effectively work with that situation when I need to access 2 different entities AND in some cases single entity multiple times.
     
  2. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    Queue up commands with EntityCommandBuffer and playback later? Use PostUpdateCommands and you don't have to create one.
     
  3. e199

    e199

    Joined:
    Mar 24, 2015
    Posts:
    101
    I was doing so when I was sure there can be only one event of the same type reference single entity.

    I can't queue commands so easily right now.

    If entity has component X, then we should increase it.
    If entity has no component X, then we should add it.

    Imagine two events would be created at a single frame for that entity.
     
  4. e199

    e199

    Joined:
    Mar 24, 2015
    Posts:
    101
    Alright, I will use hashmap and collect total amount of filtered events
    Then I will run over it and emit one command for each entity