Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

addOrSetComponent() in PostUpdateCommands

Discussion in 'Entity Component System' started by floboc, Jul 2, 2018.

  1. floboc

    floboc

    Joined:
    Oct 31, 2017
    Posts:
    91
    Since using PostUpdateCommands.addComponent() on an entity that already has a component of the same type triggers an error, I strongly suggest to add a "addOrSetComponent" function to PostUpdateCommands.
    Indeed, it is often needed to add components on the fly, for instance to tag entities. The same entity could be tagged multiple times in the same system's update and there is currently no way to check that "addComponent" was not already scheduled on that entity.

    One current workarround is to store hash sets of entities on which PostUpdateCommands.addCopmponent was already added, but then you have to do it for each type of component, and it does not seem very practical...
     
    Last edited: Jul 2, 2018
    NotaNaN, nzhangaudio, Singtaa and 4 others like this.
  2. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    Also in the same vein, RemoveComponentIfExists() would be very useful too.
     
  3. dstilwagen

    dstilwagen

    Joined:
    Mar 14, 2018
    Posts:
    36
    Here are two extension methods that should work with PostUpdateCommands
    Code (CSharp):
    1.         public static void AddOrSetComponent<T>(this EntityCommandBuffer commandBuffer, Entity entity, T component) where T : struct, IComponentData
    2.         {
    3.             var entityManager = World.Active.GetExistingManager<EntityManager>();
    4.             if(entityManager.HasComponent<T>(entity))
    5.                 commandBuffer.SetComponent(entity, component);
    6.             else
    7.                 commandBuffer.AddComponent(entity, component);
    8.         }
    9.         public static void RemoveComponentIfExists<T>(this EntityCommandBuffer commandBuffer, Entity entity) where T : struct, IComponentData
    10.         {
    11.             var entityManager = World.Active.GetExistingManager<EntityManager>();
    12.             if(entityManager.HasComponent<T>(entity))
    13.                 commandBuffer.RemoveComponent<T>(entity);
    14.         }
    This might not be the best or right way to do this so if anyone has a better way please correct me.
     
  4. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    @dstilwagen Doesn't make too much sense to use EntityManager.HasComponent() to check stuff on the CommandBuffer. For example, if the component don't exist on the entity and you call PostUpdateCommands.AddOrSetComponent() twice, you are just calling commandBuffer.AddComponent() twice, which will in turn throw an error when the CommandBuffer gets processed.

    I think some internal magic needs to happen to accommodate the proposed APIs.
     
  5. floboc

    floboc

    Joined:
    Oct 31, 2017
    Posts:
    91
    Yes, as Creepgin said, the goal here is to specifically address the case when a non-existing component might be added twice or more through EntityCommandBuffer, and you don't care overriding it if it is already present.
     
  6. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,753
    I kind of feel like if you're adding the same component more than once from the same ComponentSystem in a single update, something is a bit off on your logic or design or flow etc. and you should probably refactor.

    That said I'd like AddOrSet command anyway just as some syntax sugar so I don't have to do a HasComponent<T> check beforehand. While I use the same extension methods dstilwagen listed, I feel like they should be standard.
     
    Last edited: Jul 3, 2018
  7. gebbiz

    gebbiz

    Joined:
    May 23, 2018
    Posts:
    14
    It's not just syntax sugar. When using EntityCommandBuffer from jobs you often run multiple systems before a single barrier that playback the commands. If several systems can add/remove the same tag component you have no easy way of checking if the component is already added as the commands are not yet executed.

    Adding barriers between every system would kill multi-threading performance so the only option currently would be to hack in a global hashmap to buffer the changes manually but that doesn't really feel like optimal solution.
     
    NotaNaN likes this.
  8. Deadcow_

    Deadcow_

    Joined:
    Mar 13, 2014
    Posts:
    133
    I'd rather call it ReplaceComponentData. Like in Entitas, there is Add/Remove/Replace methods for components :rolleyes:
     
  9. floboc

    floboc

    Joined:
    Oct 31, 2017
    Posts:
    91
    "Replace" sounds more like what "Set" actually does. Replacing something implies that this thing already exists.
     
    Singtaa likes this.