Search Unity

Cannot add SharedComponentData with entity field in preview 23. Looking for workaround.

Discussion in 'Entity Component System' started by swejk, Jan 18, 2019.

  1. swejk

    swejk

    Joined:
    Dec 22, 2013
    Posts:
    20
    In the recent package update (preview.23) EntityCommandBuffer.AddSharedComponent support was dropped for SharedComponentData with entity fields.

    I very often use SharedComponentData with entity field to link units to buffs, items, visual effects etc. This is handy as I can find the buffs of specific type for specific unit by filtering component groups.
    After the update I cannot add such components to entities created at runtime so this breaks my current systems in my RPG.

    I am looking for an alternative approach. I can store the buff entities in BufferComponents on unit, but the consequence is that I cant group specific type of buffs for a unit, so I have to check every buff in buffer if it has required component. I can change the component with entity field to be ComponentData, but then I dont see efficient way of filtering buffs for specific unit.

    I have such systems implemented with SharedComponents as this, but this wont work when I wont be able to Set UnitBuff components at runtime.
    Code (CSharp):
    1. struct UnitBuff : ISharedComponentData {
    2.     public Entity Unit;
    3. }
    4.  
    5. class CreateBuffSystem : ComponentSystem {
    6.  
    7.     ComponentGroup targetsGroup = GetComponentGroup(typeof(AbilityTarget))
    8.  
    9.     void OnUpdate() {
    10.         var targets = targetsGroup.GetComponentData<AbilityTarget>();
    11.      
    12.         for(int i = 0; i < targets.Length; i++) {
    13.             var targetUnit = targets[i].Unit;
    14.          
    15.             //DOES NOT WORK
    16.             //Exception thrown:
    17.             //EntityCommandBuffer.AddSharedComponentData does not support
    18.             //shared componenents with Entity fields.
    19.             PostUpdateCommands.CreateEntity();
    20.             PostUpdateCommands.AddSharedComponent(new UnitBuff{Unit = target});
    21.             PostUpdateCommands.AddComponent(new Dispelable());
    22.         }
    23.     }
    24. }
    25.  
    26. class DispelSystem : ComponentSystem {
    27.  
    28.     ComponentGroup buffsGroup = GetComponentGroup(typeof(UnitBuff), typeof(Dispelable));
    29.     ComponentGroup dispelGroup = GetComponentGroup(typeof(DispelEffect)));
    30.  
    31.     void OnUpdate() {
    32.         var dispels = dispelGroup.GetComponentData<DispelEffect>();
    33.         for(int i = 0; i < dispels.Length; i++) {
    34.             var buffFilter = new UnitBuff { Unit = dispels[i].Target }
    35.             buffsGroup.SetFilter(buffFilter);
    36.          
    37.             var buffsEntities = buffsGroup.GetEntityArray();
    38.             for(int j = 0; j < buffsEntities.Length; j++) {
    39.                 PostUpdateCommands.DestroyEntity(buffsEntities[j]);
    40.             }
    41.         }  
    42.     }
    43. }
     
  2. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    I'd just IComponentData and just blast through all the possible entities and compare them to find the right one.

    Using tags to filter for the right type of buff first.

    Over-using shared component data like this results in massive amounts of chunks which is not good for perf.
     
  3. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    982
    I'm using shared component the same way, too.

    How can we do this for grouping that is known at runtime? You can't make new component tags on the fly.
     
  4. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    451
    You can add and remove tag components on the fly. I was using this for Invulnerable component, I was adding this to a player after he was hit.
     
  5. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    982
    What I meant was the filtering using a unique shared component that is added during runtime. You can't make a unique component tag during runtime that you can use to filter your entities. For example, in OPs code it's this line:

    Code (CSharp):
    1. PostUpdateCommands.AddSharedComponent(new UnitBuff{Unit = target});
    That's a unique shared component that is generated on the fly that is then used to filter entities using ComponentGroup.SetFilter(). How do you do that with component tags?
     
  6. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,683
    He mean creating new component types at runtime. In SCD all different value is “new” SCD type at runtime, which groups data in different chunks.
     
  7. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    451
    O ok I that sense new I did not catch that.
     
  8. JooleanLogic

    JooleanLogic

    Joined:
    Mar 1, 2018
    Posts:
    447
    Why have they forbidden this in the first place? Does it mess with ecs internals or something?
    I know they internally track all the offsets of Entity components inside other components but I haven't figured out why yet.

    I'm not advocating against Joachims point of overusing SharedComponents, but as a workaround, can you just create your own custom Entity component for use in the SharedComponent? Cast between the two with implicit operators?
     
  9. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    982
    I hope they'll revert. There certainly is a use case for it. If a developer abuses it and loses track of performance, that's his/her fault.
     
    PaulUsul likes this.
  10. swejk

    swejk

    Joined:
    Dec 22, 2013
    Posts:
    20
    Thanks for a reply.

    I have overestimated how many buffs I will have for a unit, so it is fine to iterating over all buffs to find ones linked to unit.

    I will change to IComponentData and update my systems as this.

    Code (CSharp):
    1.  
    2. class DispelSystem : ComponentSystem {
    3.     ComponentGroup buffsGroup = GetComponentGroup(typeof(UnitBuff), typeof(Dispelable));
    4.     ComponentGroup dispelGroup = GetComponentGroup(typeof(DispelEffect)));
    5.    
    6.     void OnUpdate() {
    7.         var dispels = dispelGroup.GetComponentData<DispelEffect>();
    8.         var buffsEntities = buffsGroup.GetEntityArray();
    9.         for(int i = 0; i < dispels.Length; i++) {
    10.             for(int j = 0; j < buffsEntities.Length; j++) {
    11.                 if(buffsEntities[j] == dispels[i].Target) {
    12.                     PostUpdateCommands.DestroyEntity(buffsEntities[j]);
    13.                     break;
    14.                 }
    15.             }
    16.         }  
    17.     }
    18. }
     
  11. Sylmerria

    Sylmerria

    Joined:
    Jul 2, 2012
    Posts:
    369
    I know why Unity throw an error for this case but I don't think that is a good idea.
    SharedComponentData with or without Entity field can create a lot of chunk fragments.
    A simple float field is worst than an Entity.

    In my project I use a SharedComponentData for split a demand/offer system by resources wanted. Resources are dynamics so I can't use tag. I can add a job for parsing all demand/offer and stock them in a NativeMultiHashMap but I don't know why this method is better than the other.
     
  12. nykwil

    nykwil

    Joined:
    Feb 28, 2015
    Posts:
    49
    Ran into this error. I don't know if this is the appropriate use for this but I have projectiles and each projectile has a target (for homing), it makes sense for the target component to be shared because there's only player targets (right now at least so like (2-4)) in the scene so filtering by the targets seems to make sense.
     
  13. nykwil

    nykwil

    Joined:
    Feb 28, 2015
    Posts:
    49
    Can I bump this for some advice on how to implement around this. A target entity in a shared component for my projectiles (there's only 2-3 possible targets) seems ideal for a shared component.
     
  14. zachlindblad

    zachlindblad

    Joined:
    Sep 29, 2016
    Posts:
    39
    Also having this issue with what I think is a reasonable use case:
    Have Entities with dynamic buffers representing grids, I create entities for edits I want to make into those grids to process asynchronously in JobComponentSystems, and need some way to group them with their parent grids.
    From a data perspective it makes sense to have edits of the same grid in the same chunk.
    In game, large groups of edits all at once that need to be processed ASAP is the normal use case, so manual "blasting" is probably going to result in lag. And if the edits are all grouped in their own chunks, I can finish updating individual groups independent of each other, without having to wait for all edits to be processed to do things like generating meshes.
    I understand this feature could be abused, but this format is the correct one for me, I'm using a hack of an additional ID tied to my grid to use as a filter for a work around, but it feels like an arbitrary limitation.

    (also, as an aside, there's a misspelling in the error message:
    EntityCommandBuffer.AddSharedComponentData does not support shared componenents with Entity fields.")