Search Unity

Bug or Normal behavior? preview.30

Discussion in 'Entity Component System' started by echeg, Apr 9, 2019.

  1. echeg

    echeg

    Joined:
    Aug 1, 2012
    Posts:
    90
    1 Create Entity with ComponentA and ComponentB
    2 Create system with group (EntityQuery)
    SetFilterChanged(ComponentType.ReadOnly<ComponentA>()
    3 Add or Remove ComponentC to this entity.
    4 System will trigger ComponentA is changed??? Why?

    Example code:

    Code (CSharp):
    1. public class AddRemoveComponentC : MonoBehaviour
    2. {
    3.    
    4.     private EntityQuery compGroup;
    5.     void Start()
    6.     {
    7.         compGroup = World.Active.EntityManager.CreateEntityQuery(ComponentType.ReadOnly<ComponentB>());
    8.     }
    9.  
    10.     void OnGUI()
    11.     {
    12.         if (GUI.Button(new Rect(0, 0, 200, 200), "Add/Remove C"))
    13.         {
    14.             var es = compGroup.ToEntityArray(Allocator.TempJob);
    15.             foreach (var entity in es)
    16.             {
    17.                 if (World.Active.EntityManager.HasComponent<ComponentC>(entity))
    18.                 {
    19.                     World.Active.EntityManager.RemoveComponent<ComponentC>(entity);                  
    20.                 }
    21.                 else
    22.                 {
    23.                     World.Active.EntityManager.AddComponentData(entity, new ComponentC());
    24.                 }
    25.             }
    26.  
    27.             es.Dispose();
    28.         }
    29.     }
    30. }
    31.  
    32. public struct ComponentA : IComponentData
    33. {
    34. }
    35.  
    36. public struct ComponentB : IComponentData
    37. {
    38. }
    39.  
    40. public struct ComponentC : IComponentData
    41. {
    42. }
    43.  
    44. [UpdateInGroup(typeof(PresentationSystemGroup))]
    45. public class WatchChangeComponentSystem : ComponentSystem
    46. {      
    47.     private EntityQuery _notifyGroup;
    48.        
    49.     protected override void OnCreateManager()
    50.     {
    51.         _notifyGroup = GetEntityQuery(ComponentType.ReadOnly<ComponentA>());
    52.         _notifyGroup.SetFilterChanged(ComponentType.ReadOnly<ComponentA>());
    53.  
    54.         var e = EntityManager.CreateEntity();
    55.         EntityManager.AddComponentData(e, new ComponentA());
    56.         EntityManager.AddComponentData(e, new ComponentB());
    57.     }
    58.        
    59.     protected override void OnUpdate()
    60.     {
    61.         if (_notifyGroup.CalculateLength() == 0) return;
    62.         Debug.Log("NotificationSpellCreate "+_notifyGroup.CalculateLength());      
    63.     }
    64. }

    Its normal? I dont rewrite ComponentA but system will trigger.
    If its normal how use SetFilterChanged? Any person anywhere in the project can add or remove any component from entity. And this will trigger all change filters on this entity...
     
  2. Soaryn

    Soaryn

    Joined:
    Apr 17, 2015
    Posts:
    328
    Unsure how to answer that to be honest. I do know the "Active world" is not likely your target per system here though. You'd be better off accessing the System's EntityManager.

    But as far as I know, if you make a component group, if that group ever changes, then the system with said component group will also update as it doesn't know what you are doing with it, just that you were interested with it at start.
     
  3. echeg

    echeg

    Joined:
    Aug 1, 2012
    Posts:
    90
    Active world - its for test. I have same behavior if use "System's EntityManager."

    Where I change group? If I have group of componentsA, why group change if I add componentC?
     
  4. echeg

    echeg

    Joined:
    Aug 1, 2012
    Posts:
    90
    For example, the simplest task when creating an Entity (from SystemA) is to add an additional componentB to it (from SystemB)
    If SetFilterChanged is broken and useless I can not create a systemB like:
    Code (CSharp):
    1. _notifyGroup = GetEntityQuery(ComponentType.ReadOnly<ComponentA>());
    2.         _notifyGroup.SetFilterChanged(ComponentType.ReadOnly<ComponentA>());
    3.  
    4. Execute ...
    5. EntityManager.AddComponentB
    6. ...
    Because any other system changes(add\remove component) this Entity, it will come to double call this logic.
    And the only safe option to implement such logic is to use SystemStateComponents.
     
  5. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    SetFilterChanged is per chunk, not per entity.
    and a structural change (e.g. adding a component) will change which chunk the entity is in, therefore the filter will trigger.

    if you also want a per-entity filter, you need to track them with state components (or in your case, ComponentType.Exclude<B>)
     
  6. echeg

    echeg

    Joined:
    Aug 1, 2012
    Posts:
    90
    Like?
    Code (CSharp):
    1.         var query = new EntityQueryDesc()
    2.         {
    3.             None = new ComponentType[]
    4.             {
    5.                 typeof(ComponentB),
    6.             },
    7.             All = new ComponentType[]
    8.             {
    9.                 ComponentType.ReadOnly<ComponentA>(),
    10.             }              
    11.         };
    In this case when I add ComponentB all ok.
    But when I remove ComponentB -> this will _notifyGroup.SetFilterChanged(ComponentType.ReadOnly<ComponentA>()); trigger again...

    And in real world with 5+ ppl on project I dont know about components B and C add|remove. I create logic with component A, and some code other ppl completely broken my code without any reference to A component...

    SystemStateComponents yes I may create somthing like

    Code (CSharp):
    1.     public class CreateDestroyComponantA: JobComponentSystem
    2.     {                    
    3.           [ExcludeComponent(typeof(ComponantAState))]
    4.         struct ProcessAddedEntities : IJobProcessComponentDataWithEntity<ComponentA>
    5.         {
    6.             [ReadOnly]  public EntityCommandBuffer.Concurrent ecb;
    7.  
    8.             public void Execute(Entity entity, int index, [ChangedFilter] [ReadOnly] ref ComponentA data)
    9.             {
    10.                 ecb.AddComponent(index, entity, new ComponantAState());    
    11. // Only one time on creation logic          
    12.             }
    13.         }
    14.  
    15.         [ExcludeComponent(typeof(ComponentA))]
    16.         struct ProcessRemovedEntities : IJobProcessComponentDataWithEntity<ComponantAState>
    17.         {
    18.             public void Execute(Entity entity, int index, [ReadOnly] ref ComponantAStatedata)
    19.             {
    20.                 ecb.RemoveComponent<ComponantAState>(index, entity);                                            
    21. // Only one time on destroy logic          
    22.             }
    23.         }
    24.  
    25.         protected override JobHandle OnUpdate(JobHandle inputDeps)
    26.         {
    27.             var commandBuffer = _commandBufferSystem.CreateCommandBuffer().ToConcurrent();
    28.             var job1 = new ProcessAddedEntities
    29.             {
    30.                 ecb = commandBuffer
    31.             };          
    32.  
    33.             var handle = job1.Schedule(this, inputDeps);
    34.             handle.Complete();
    35.          
    36.             var job2 = new ProcessRemovedEntities
    37.             {
    38.                 ecb = commandBuffer,
    39.             }.Schedule(this, handle);
    40.             job2.Complete();
    41.          
    42.             return job2;
    43.         }
    44.     }
    45. }
    But this pattern more complicated. And its work only for create\remove componentA.
    What if I need to call logic only when the value of the component changes?
    The only thing that comes to my mind is to make a SystemStateComponentALastValue and on every execute check SystemStateComponentALastValue != ComponentA. But it ugly and have many performance issue...
     
  7. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    exactly that + change filter.

    filter will exclude unchanged chunks, then you search which entity actually changed inside the chunk.

    or you centralize all the modifications of A then you can react to it as you want (e.g. create an additional entity that tracks the change, then process and destroy it)
     
    LesterZw and echeg like this.