Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Usage of IEnableableComponent

Discussion in 'Entity Component System' started by TP-Fab, Mar 1, 2023.

  1. TP-Fab

    TP-Fab

    Joined:
    Jul 10, 2019
    Posts:
    37
    Hi there,

    Got a couple questions about IEnableableComponent as replacement of tag components:

    #1 Is there a way to use them with Entities.ForEach in SystemBase? I tried WithAll<> and adding the component to the description, but it doesn't seem to care if that's disabled.

    #2 what's the way to use them in ISystem with the least amount of boilerplate code? I am currently doing this but been wondering if there's anything that doesn't require the empty variable definition

    Code (CSharp):
    1. foreach ( var (data, _) in SystemAPI.Query< RefRW< ECData >,  EnabledRefRO< ECEnableableComponent > >() )
    2. {
    3. }
    #1 would be ideal to limit immediate refactoring (200+ existing systems), but #2 can definitely be helpful too :)

    Thanks!
     
  2. suity447

    suity447

    Joined:
    Oct 18, 2022
    Posts:
    33
    For 2#: SystemAPI.Query can youse all EntityQuery modifiers:
    Code (CSharp):
    1. foreach (var data in SystemAPI.Query<RefRW<ECData>>().WithAll<ECEnableableComponent>()) // None/Absent/Disabled/Options etc
    2. {
    3. }
     
  3. TP-Fab

    TP-Fab

    Joined:
    Jul 10, 2019
    Posts:
    37
    Problem is that WithAll<ECEnableableComponent> doesn't filter out disabled components, which is also why I can't use it in Entities.ForEach :(

    WithAll only seems to check the existence of the component, unlike what EnabledRefRO does (vs RefRO)
     
    Last edited: Mar 2, 2023
  4. cort_of_unity

    cort_of_unity

    Unity Technologies

    Joined:
    Aug 15, 2018
    Posts:
    97
    That is surprising; WithAll<Component> is intended to filter out entities with Component disabled. Are you seeing this behavior in the latest Entities 1.0 prerelease package?
     
    BackgroundMover likes this.
  5. TP-Fab

    TP-Fab

    Joined:
    Jul 10, 2019
    Posts:
    37
    Hi Cort, I'm using 1.0.0-pre.15 (custom). Strangely when creating a very small sample set of systems (below) today, I do get the expected result as you mention (disabled components filtered out.

    Thanks for confirming this is supposed to work, that means I can search what I've done wrong with confidence :)

    Code (CSharp):
    1.             var manager = Unity.Entities.World.DefaultGameObjectInjectionWorld.EntityManager;
    2.             var enabled = manager.CreateEntity( typeof( ECData ), typeof( ECEnableCompo ) );
    3.             manager.SetName( enabled, "enabled" );
    4.  
    5.             var disabled = manager.CreateEntity( typeof( ECData ), typeof( ECEnableCompo ) );
    6.             manager.SetName( disabled, "disabled" );
    7.             manager.SetComponentEnabled< ECEnableCompo >( disabled, false );
    8.  
    Code (CSharp):
    1. public struct ECData:IComponentData
    2.     {
    3.         public int a;
    4.     }
    5.  
    6.     public struct ECEnableCompo : IComponentData, IEnableableComponent
    7.     {
    8.  
    9.     }
    10.  
    11.     public partial struct ESTest : ISystem
    12.     {
    13.         public void OnCreate( ref SystemState state )
    14.         {
    15.         }
    16.  
    17.         public void OnDestroy( ref SystemState state )
    18.         {
    19.         }
    20.  
    21.         public void OnUpdate( ref SystemState state )
    22.         {
    23.             foreach ( var (data, entity) in SystemAPI.Query< RefRW< ECData > >().WithAll< ECEnableCompo >().WithEntityAccess() )
    24.             {
    25.                 Debug.Log( $"[ESTest] {entity.GetName()} = {Unity.Entities.World.DefaultGameObjectInjectionWorld.EntityManager.IsComponentEnabled< ECEnableCompo >( entity )}" );
    26.             }
    27.         }
    28.     }
    29.     public partial class  ESTestSystemBase : SystemBase
    30.     {
    31.         protected override void OnUpdate()
    32.         {
    33.             Entities.WithAll<ECEnableCompo>().ForEach( ( Entity entity, ref ECData data ) =>
    34.             {
    35.                 Debug.Log( $"[ESTestSystemBase] {entity.GetName()} = {Unity.Entities.World.DefaultGameObjectInjectionWorld.EntityManager.IsComponentEnabled< ECEnableCompo >( entity )}" );
    36.  
    37.             }).WithoutBurst().Run();
    38.         }
    39.     }
    Result:
    [ESTest] [enabled:274] = True
    [ESTestSystemBase] [enabled:274] = True
    [ESTest] [enabled:274] = True
    [ESTestSystemBase] [enabled:274] = True
    [ESTest] [enabled:274] = True
    [ESTestSystemBase] [enabled:274] = True
    [ESTest] [enabled:274] = True
    ....
     
  6. TP-Fab

    TP-Fab

    Joined:
    Jul 10, 2019
    Posts:
    37
    Still an absolute mystery. While this works perfectly in a sample project, I manage to get unfiltered disabled components in a specific system. Everything is running on mainthread... (note that if uncommenting the !IsComponentEnabled condition, it does recognize that shouldn't go further)
    upload_2023-3-3_11-37-16.png
     
  7. cort_of_unity

    cort_of_unity

    Unity Technologies

    Joined:
    Aug 15, 2018
    Posts:
    97
    I'm curious if this is a problem in the EntityQuery automatically generated by Entities.ForEach and SystemAPI.Query, or a problem with the enableable-component-matching logic. Would it be possible to modify the failing case that it uses an explicitly-created EntityQuery instead of a something automatically generated, or would that be too intrusive?
     
  8. TP-Fab

    TP-Fab

    Joined:
    Jul 10, 2019
    Posts:
    37
    I'm in the middle of switching that to an idiomatic foreach & ISystem, which does seem to work as expected (without changing anything else around). So yes most likely some combo of ForEach & enableable that doesn't play well together.

    My hope was to avoid that switch, since ISystem isn't able to deal with structural changes, which means quite a bite of rework to get them all in the right order
     
  9. cort_of_unity

    cort_of_unity

    Unity Technologies

    Joined:
    Aug 15, 2018
    Posts:
    97
    I believe I've found and fixed this issue. The problem was that foreach() over a query with enableable components was not iterating over the correct entities (and thus some of the entities it processed won't have the component enabled).

    As a workaround until the next Entities package release, use IJobEntity.Run(query) instead of foreach(query) for queries with enableable components.
     
    TP-Fab, Occuros and JesOb like this.
  10. TP-Fab

    TP-Fab

    Joined:
    Jul 10, 2019
    Posts:
    37
    Oh! Now that's surprising, as ISystem foreach has been giving me the proper results (rework complete), and it was SystemBase Entities.ForEach that was broken.

    Also, after reworking a majority of the systems I was looking at to ISystem, the few ones that are still SystemBase now work properly. So clearly my systems were doing a specific combination of things that didn't go well with Entities.Foreach and IEnableableComponent
     
  11. cort_of_unity

    cort_of_unity

    Unity Technologies

    Joined:
    Aug 15, 2018
    Posts:
    97
    Oh hey, sure enough; I can reproduce the issue myself:
    Code (CSharp):
    1. public struct MyComponent : IComponentData { }
    2. public struct MyEnableableTag : IComponentData, IEnableableComponent { }
    3. public partial class EnabledComponentInEntitiesForEachTest : SystemBase
    4. {
    5.     protected override void OnCreate()
    6.     {
    7.         var enabled = EntityManager.CreateEntity( typeof( MyComponent ), typeof( MyEnableableTag ) );
    8.         EntityManager.SetName( enabled, "enabled" );
    9.         var disabled = EntityManager.CreateEntity( typeof( MyComponent ), typeof( MyEnableableTag ) );
    10.         EntityManager.SetName( disabled, "disabled" );
    11.         EntityManager.SetComponentEnabled< MyEnableableTag >( disabled, false );
    12.     }
    13.     protected override void OnUpdate()
    14.     {
    15.         Entities.WithAll<MyEnableableTag>().ForEach((Entity e) =>
    16.             {
    17.                 if (!EntityManager.IsComponentEnabled<MyEnableableTag>(e))
    18.                 {
    19.                     Debug.Log($"Entity {e} name='{EntityManager.GetName(e)}' should not be processed by this EFE");
    20.                 }
    21.             }).WithStructuralChanges().WithoutBurst()
    22.             .Run();
    23.     }
    24. }
    We're on it!
     
    TP-Fab likes this.
  12. cort_of_unity

    cort_of_unity

    Unity Technologies

    Joined:
    Aug 15, 2018
    Posts:
    97
    It seems the .WithStruturalChanges() on the loop that's preventing the enableable components from being correctly filtered by the Entities.ForEach(). I'm working on a fix, but in the meantime, there's a potential workaround to try.
     
    TP-Fab likes this.
  13. TP-Fab

    TP-Fab

    Joined:
    Jul 10, 2019
    Posts:
    37
    Good catch, didn't notice the WithStructuralChanges could have an impact on that. I did remove all these in my latest version by introducing a native list of deferred changes + ECBs, that's probably why I don't see the issue anymore even with EFE. Thanks for looking into it!