Indeed, documentation says that disabled components shoud be excluded from all entity queries, but your example proves the opposite. So, to solve this puzzle we need to go deeper (c).
Look into code that is generated for our OnUpdate() function. Firstly modify the body a little:
Entities.WithoutBurst().ForEach((Entity entity, in TestTagCo testTagCo) =>
{
Debug.Log("TestSystem is running");
Debug.Log( EntityManager.IsComponentEnabled<TestTagCo>(entity));
}).Run();
I replaced WithStructuralChanges with WithoutBurst to try no structural changes version first. We got in result of the codegeneration is:
struct TestSystem_DBD3F09_LambdaJob_0_Job : Unity.Entities.IJobChunk
{
[global::System.Runtime.CompilerServices.CompilerGenerated]
public void Execute(in ArchetypeChunk chunk, int batchIndex, bool useEnabledMask, in Unity.Burst.Intrinsics.v128 chunkEnabledMask)
{
...
if (!useEnabledMask)
{
...
}
else
{
...
}
}
}
I have skipped most of the code to highlight essential parts. We can see several important things here:
- Entites.ForEach is transformed into IJobChunk job
- Enableable components correctly handled by IJobChunk.Execute function (useEnabledMask, chunkEnabledMask variables usage)
Now we will modify our code back as in your example:
Entities.WithStructuralChanges().ForEach((Entity entity, in TestTagCo testTagCo) =>
{
Debug.Log("TestSystem is running");
Debug.Log( EntityManager.IsComponentEnabled<TestTagCo>(entity));
}).Run();
And codegen:
struct TestSystem_DBD3F09_LambdaJob_0_Job
{
public void RunWithStructuralChange(Unity.Entities.EntityQuery query)
{
...
var mask = query.GetEntityQueryMask();
Unity.Entities.InternalCompilerInterface.UnsafeCreateGatherEntitiesResult(ref query, out var gatherEntitiesResult);
...
try
{
int entityCount = gatherEntitiesResult.EntityCount;
for (int entityIndex = 0; entityIndex != entityCount; entityIndex++)
{
var entity = Unity.Entities.InternalCompilerInterface.UnsafeGetEntityFromGatheredEntities(ref gatherEntitiesResult, entityIndex);
if (mask.MatchesIgnoreFilter(entity))
{
...
}
}
}
finally
{
Unity.Entities.InternalCompilerInterface.UnsafeReleaseGatheredEntities(ref query, ref gatherEntitiesResult);
}
}
}
Now we have completely different code:
- It is ordinary struct and not a job
- Entities loop is very different than what we saw in previous case
And to solve our puzzle we should look into entites query processing code. Here we have a mask from query.GetEntityQueryMask() and entity filtering by if (mask.MatchesIgnoreFilter(entity)). Documentation for this method says: "
[Returns: True if the entity would be returned by the EntityQuery (ignoring any filtering or enableable components)](Method MatchesIgnoreFilter | Entities | 1.0.16)
".
Mystery solved: the codegeneration with WithStructuralChanges attribute usage will generate a code that does not respect enableable flag in their entity queries and internal loops.