Search Unity

Add a component if none already exist ?

Discussion in 'Data Oriented Technology Stack' started by floboc, Apr 13, 2018.

  1. floboc

    floboc

    Joined:
    Oct 31, 2017
    Posts:
    89
    I have a system that injects two groups of entities. For each entity in the first group (groupA), it iterates other all the other entities of the second group (groupB) and under some condition try to add a component to it to signal an event.

    Thus, in some cases, it can occur that two entities of groupA try to add an event to the same entity in groupB.
    In that case, I would like to only keep the first or the last event added (I don't care about multiple events).

    Since entities must not be modified while iterating a group, I used the PostUpdateCommand to add this event component has such :

    Code (CSharp):
    1.  
    2. public class MySystem : ComponentSystem
    3. {
    4.     public struct GroupA
    5.     {
    6.         public int Length;
    7.         [ReadOnly] public ComponentDataArray<SomeComponent> Filter;
    8.         public EntityArray Entities;
    9.     }
    10.    
    11.    public struct GroupB
    12.     {
    13.         public int Length;
    14.         [ReadOnly] public ComponentDataArray<SomeOtherComponent> Filter;
    15.         public EntityArray Entities;
    16.     }
    17.  
    18.     [Inject] private GroupA groupA;
    19.     [Inject] private GroupB groupB;
    20.  
    21.     protected override void OnUpdate()
    22.     {
    23.         for (int i = 0; i < groupA.Length; i++)
    24.         {
    25.             for (int j = 0; j < groupB.Length; j++)
    26.            {
    27.                if (some_condition)
    28.                {
    29.                    if (!EntityManager.HasComponent<MyEvent>(groupB.Entities[j]))
    30.                    {
    31.                        PostUpdateCommands.AddComponent(groupB.Entities[j], new MyEvent {});
    32.                    }
    33.                }
    34.            }
    35.         }
    36.     }
    37. }
    38.  
    So of course, EntityManager.HasComponent does not prevent adding the event twice to the same entity since they are added in a PostUpdateCommands.

    Is there some way to use PostUpdateCommands to not add a component if it was already added to the commands, or to override it if it was already added ? I tried to use "SetComponentData", but it doesn't create a component if it does not exist.

    I know another way would be to store an array of all entities that should have the event added, and then loop over the array to add the component after going through all the entities, but I am looking for a better way if available.


    Thank you !
     
  2. Spy-Shifty

    Spy-Shifty

    Joined:
    May 5, 2011
    Posts:
    529
    just a tipp, don't assign event Component directly to that entity.

    I would create an new event entity and add the follwing components to it:

    Code (CSharp):
    1. struct SomeEventTag : IComponentData  {
    2.  
    3.     public Entity Target;
    4.     public Entity Sender; // only if required
    5. }
    6.  
    7. struct  SomeEventData1 : IComponentData {
    8.     public int SomeData;
    9. }
    10.  
    11. struct  SomeEventData2 : IComponentData {
    12.     public int SomeData;
    13. }
    14. ...
    in your systems just do the following (more like pseudo code...)
    Code (CSharp):
    1. public class SomeSystemToSendEvent : ComponentSystem {
    2.  
    3.  
    4.      public override void OnUpdate() {
    5.           EntityCommandBuffer commandBuffer = CreateCommandBuffer();
    6.  
    7.            for(int i = 0; i<dataGroup1.Length;i++) {
    8.                for(int j = 0; j<dataGroup2.Length;j++) {
    9.                 //On some condition
    10.                 commandBuffer.CreateEntity()
    11.                 commandBuffer.SetComponent(new SomeEventTag {
    12.                          Target = dataGroup1.entities[i],
    13.                          Sender = dataGroup2.entities[j]
    14.            });
    15.                 commandBuffer.SetComponent(new SomeEventData1{
    16.                          SomeData= // some data
    17.            });
    18.     }
    19.  
    20. }
    Your event handling system can than process all events
    you then won't miss multiple events on the same entity and also have the abillity to check if there are more then one event on the same entity...

    (Again its more like pseude code...)
    Code (CSharp):
    1. public class SomeSystemToHandleEvent : ComponentSystem {
    2.    
    3.      struct EventData {
    4.            public ComponentDataArray<SomeEventTag> eventTags;
    5.            public ComponentDataArray<SomeEventData1> someEventData1;
    6.            public EntityArray entities;
    7.            public int Length;
    8.      }
    9.      [Inject] EventData eventData;
    10.      [Inject] ComponentDataFromEntity<SomeOtherData> someOtherData; // mybe this won't work this way
    11.      public override void OnUpdate() {
    12.           EntityCommandBuffer commandBuffer = CreateCommandBuffer();
    13.  
    14.            for(int i = 0; i<eventData.Length;i++) {
    15.                 someOtherData[eventData.entities[i]] = new SomeOtherData {
    16.                             Data = eventData.someEventData1[i].SomeData
    17.                 };
    18.                 commandBuffer.DestroyEntity(eventData.entities[i]);
    19.            }
    20.     }
    21.  
    22. }

    You can also use PostUpdateCommands instead of creating a new command buffer...
     
  3. floboc

    floboc

    Joined:
    Oct 31, 2017
    Posts:
    89
    Very useful answer, thanks !
     
  4. floboc

    floboc

    Joined:
    Oct 31, 2017
    Posts:
    89
    By the way, If I wanted to perform some modification on a component of the entity that sends an event, what is the best way using the structure you suggested ?
    Should I iterate other all the events and all the entities that have the component that I want to modify and change the component if the event's entity matches the component's entity ?
    Or should I use ComponentDataFromEntity ? (I am not quite sure how to use this however, what happens if the entity has not the component ?)

    Thanks !

    Edit: sorry I see that you already explained this in the last part of your answer.
     
    Last edited: Apr 14, 2018
  5. Spy-Shifty

    Spy-Shifty

    Joined:
    May 5, 2011
    Posts:
    529
    Well, it depends on what you wanna archive and on your systems. If this isn't a regular case (rarely event) and you wanne do this from multiple access points, would I go with Events. If this happens highly frequently (every frame) and only from that system, then do it directly.

    Use ComponentDataFromEntity<MyComponent>
    ComponentDataFromEntity is an array of "MyComponents" of all entities which has this component attached.
    (This is recommended by the Unity dev team)

    The index operator is just the entity. Well what happens if an entity doesn't has that specific component.
    I don't know :D I think it will throw an out of range exception...!
     
    floboc likes this.