3D Quadrant System to avoid looping through all entities

Hi, i made this pattern for 3D Quadrant System to avoid looping through all entities for each entity
The problem this pattern makes EndSimulationEntityCommandBufferSystem >>Wait For Job Group Id>> very high for 1000 Entities
Why does it get so high even though with Burst and Scheduling?
is is there a better way to do this?
maybe a better way to group entities instead of changing Queries ?


i have two Systems,
1- adding and removing tag and Depending on the Coordinates, so it changing Queries
2- looping in Queries (by tags) and finding target by distance
As follows:
system 1 :

public class QueriesSystem : JobComponentSystem
{
    public float time;
    protected EndSimulationEntityCommandBufferSystem m_EndSimulationEcbSystem;
    protected override void OnCreate()
    {
        base.OnCreate();
        m_EndSimulationEcbSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    }
    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        var ecb = m_EndSimulationEcbSystem.CreateCommandBuffer().ToConcurrent();
        float deltaTime = Time.DeltaTime;
        JobHandle jobhandle = Entities.ForEach((Entity entity, int entityInQueryIndex,ref Place place, in Translation translation) =>
        {
           
                if (translation.Value.x > 0 && translation.Value.x < 1 && translation.Value.y > 0 && translation.Value.y < 1&& translation.Value.z > 0 && translation.Value.z < 1)
                {
                    place.preplace = place.curplace;
                    place.curplace = 0;
                    if (place.curplace != place.preplace)
                    {
                        switch (place.prevplace)
                        {
                            case 0:
                                ecb.RemoveComponent<RedBoxTag>(entityInQueryIndex, entity);
                                break;
                            case 1:
                                ecb.RemoveComponent<GreenBoxTag>(entityInQueryIndex, entity);
                                break;
                           ETC....
                        }
                        ecb.AddComponent(entityInQueryIndex, entity, new RedBoxTag());

                    }

                }
                else if (translation.Value.x > 0 && translation.Value.x < 1 && translation.Value.y > 0 && translation.Value.y < 1 && translation.Value.z > 1 && translation.Value.z < 2)
                {
                   place.preplace = place.curplace;
                   place.curplace = 1;
                switch (place.prevplace)
                    {
                    case 0:
                        ecb.RemoveComponent<RedBoxTag>(entityInQueryIndex, entity);
                        break;
                    case 1:
                        ecb.RemoveComponent<GreenBoxTag>(entityInQueryIndex, entity);
                        break;
                }
                    ecb.AddComponent(entityInQueryIndex, entity, new GreenBoxTag());

                }
                else if (ETC...)
                {
                  ETC...
                }

        }).Schedule(inputDeps);

        m_EndSimulationEcbSystem.AddJobHandleForProducer(jobhandle);
        return jobhandle;



    }
}

system 2 :

public class loopSystem : JobComponentSystem
{
    protected EndSimulationEntityCommandBufferSystem m_EndSimulationEcbSystem;
    EntityQuery triggers;
    protected override void OnCreate()
    {
        base.OnCreate();
        m_EndSimulationEcbSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    }
    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        var GreenBoxTags = GetEntityQuery(ComponentType.ReadOnly<GreenBoxTag>());
        var RedBoxTags = GetEntityQuery(ComponentType.ReadOnly<RedBoxTag>());
        NativeArray<Entity> GreenBoxTagsAR = GreenBoxTags.ToEntityArray(Allocator.TempJob);
        NativeArray<Entity> RedBoxTagsAR = RedBoxTags.ToEntityArray(Allocator.TempJob);

        ComponentDataFromEntity<Translation> translationsFE = GetComponentDataFromEntity<Translation>(true);

        JobHandle jobhandle = Entities.WithAny<AntTag, QueenTag>().WithDeallocateOnJobCompletion(GreenBoxTagsAR).WithDeallocateOnJobCompletion(RedBoxTagsAR).ForEach((Entity entity, int entityInQueryIndex,in Place place) =>
            {

                switch (place.curplace)
                {
                    case 0:

                        for (int i = 0; i < RedBoxTagsAR.Length; i++)
                        {
                            Entity trigentity = RedBoxTagsAR[i];
                                float3 pos = translationsFE[trigentity].Value;
                                float distance = math.distance(translationsFE[entity].Value, pos);
                               if (distance >= 0.1 && distance <= 0.5)
                               {

                               }
                        break;
                    case 1:
                        for (int i = 0; i < GreenBoxTagsAR.Length; i++)
                        {
                            Entity trigentity = GreenBoxTagsAR[i];
                                float3 pos = translationsFE[trigentity].Value;
                                float distance = math.distance(translationsFE[entity].Value, pos);
                            if (distance >= 0.1 && distance <= 0.5)
                            {

                            }
                        break;
                    case 2:
                        ETC...
                        break;
                }
            }).Schedule(inputDeps);
        m_EndSimulationEcbSystem.AddJobHandleForProducer(jobhandle);
        return jobhandle;
    }
}

This is the exact purpose of ISharedComponentData: Shared component data | Entities | 0.10.0-preview.6

1 Like

Show Timeline view of Profiler

Using a Native Container storing entity references can offer you a much higher resolution grid without the cost of moving entities around. A higher resolution grid allows for far fewer checks in your loop, which will drastically outweigh the additional random access cost.

1 Like

brunocoimbra i will check it thank you !

1 Like

DreamingImLatios Sounds good thanks !
How can I use Native Container to storing entity, is it like a system that inserts entities by their positions into arrays inside IComponentData or IBufferElementData ? or how ?

NativeArray arrayStoringEntityReferences

I usually have alongside it a NativeArray which stores the ranges of each grid cell.

1 Like

brunocoimbra I triedISharedComponentData and I got a better performance, but there is a wierd problem when i start the game with just 3 entities the arrays t0ar.Length and t1ar.Length become about 50 ,I think there is a duplicate copies for this 3 entity inside the arrays, also i’m not sure if i’m using filters in right way, please can you tell me what am I doing wrong?

updated code :

public class QueriesSystem : JobComponentSystem
{
    public float time;
    protected EndSimulationEntityCommandBufferSystem m_EndSimulationEcbSystem;
    protected override void OnCreate()
    {
        base.OnCreate();
        m_EndSimulationEcbSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    }
    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        var ecb = m_EndSimulationEcbSystem.CreateCommandBuffer().ToConcurrent();
        float deltaTime = Time.DeltaTime;
        JobHandle jobhandle = Entities.ForEach((Entity entity, int entityInQueryIndex, ref Place place, in Translation translation) =>
        {

        if (translation.Value.x > 0 && translation.Value.x < 1 && translation.Value.y > 0 && translation.Value.y < 1 && translation.Value.z > 0 && translation.Value.z < 1)
        {
            place.preplace = place.curplace;
            place.curplace = 0;
            if (place.curplace != place.preplace)
            {
                    ecb.SetSharedComponent(entityInQueryIndex, entity, new QuadrantISharedComponent { T = 0 });
            }

        }
        else if (translation.Value.x > 0 && translation.Value.x < 1 && translation.Value.y > 0 && translation.Value.y < 1 && translation.Value.z > 1 && translation.Value.z < 2)
        {
            place.preplace = place.curplace;
            place.curplace = 1;

                if (place.curplace != place.preplace)
                {
                    ecb.SetSharedComponent(entityInQueryIndex, entity, new QuadrantISharedComponent { T = 1 });
                }

            }
        else if (ETC...)
                {
            ETC...
                }

    }).Schedule(inputDeps);

    m_EndSimulationEcbSystem.AddJobHandleForProducer(jobhandle);
        return jobhandle;
    }
}

system 2 :

public class loopSystem : JobComponentSystem
{
    protected EndSimulationEntityCommandBufferSystem m_EndSimulationEcbSystem;
    EntityQuery triggers;
    protected override void OnCreate()
    {
        base.OnCreate();
        m_EndSimulationEcbSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    }
    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        var triggers = GetEntityQuery(typeof(QuadrantISharedComponent));
        triggers.SetSharedComponentFilter(new QuadrantISharedComponent { T = 0 });
        NativeArray<Entity> t0ar = triggers.ToEntityArray(Allocator.TempJob);
        triggers.SetChangedVersionFilter(typeof(QuadrantISharedComponent));
        triggers.SetSharedComponentFilter(new QuadrantISharedComponent { T = 1 });
        NativeArray<Entity> t1ar = triggers.ToEntityArray(Allocator.TempJob);

        ComponentDataFromEntity<Translation> translationsFE = GetComponentDataFromEntity<Translation>(true);

        JobHandle jobhandle = Entities.WithAny<AntTag, QueenTag>().WithDeallocateOnJobCompletion(GreenBoxTagsAR).WithDeallocateOnJobCompletion(RedBoxTagsAR).ForEach((Entity entity, int entityInQueryIndex, in Place place) =>
        {

            switch (place.curplace)
            {
                case 0:

                    for (int i = 0; i < t0ar.Length; i++)
                    {
                        Entity trigentity = t0ar[i];
                        float3 pos = translationsFE[trigentity].Value;
                        float distance = math.distance(translationsFE[entity].Value, pos);
                        if (distance >= 0.1 && distance <= 0.5)
                        {

                        }
                        break;
                    case 1:
                    for (int i = 0; i < t1ar.Length; i++)
                    {
                        Entity trigentity = t1ar[i];
                        float3 pos = translationsFE[trigentity].Value;
                        float distance = math.distance(translationsFE[entity].Value, pos);
                        if (distance >= 0.1 && distance <= 0.5)
                        {

                        }
                        break;
                    case 2:
                    ETC...
                        break;
            }
        }).Schedule(inputDeps);
        m_EndSimulationEcbSystem.AddJobHandleForProducer(jobhandle);
        return jobhandle;
    }
}

Seems like you are using in the right way, if you have only 3 entities and there is 50 in the entity array then there is something clearly wrong there… did you double to check to verify if that’s the actual case?

1 Like

Yes, it works fine now!
Is there a way to combine or merging two NativeArrays in one or two Query like if we want to combine

NativeArray<Entity>   tCar  =  t0ar.Concat(t1ar).ToArray();

then use

for (int i = 0; i < tCar.Length; i++)
{}

or use SetSharedComponentFilter to get two different values

var triggers = GetEntityQuery(typeof(QuadrantISharedComponent));
        triggers.SetSharedComponentFilter(new QuadrantISharedComponent { T = 0 }||new QuadrantISharedComponent { T = 1 });
        NativeArray<Entity> tCar = triggers.ToEntityArray(Allocator.TempJob);

I am not sure if there is a built-in method for that, but you can create a third native array to put the combination by simply creating a new NativeArray with the size being the sum of both arrays sizes and then filling this array with the values.

1 Like

Did you try to create 2 queries (one for each filter) and then combine them before executing the combined version?

1 Like

WAYN_Group oh sorry i’m not sure what do you mean can you show me an example! , my goal here is to get a Native Array containing Entities of two Queries, the only way I know so far is to manually insert the entities from two arrays into a third native array as Brunocoimbra mentioned

@Michelle_Ca , sorry I got confused with the combination of 2 entity query derscriptions.

var query0 = new EntityQueryDesc
{
   All = new ComponentType[] {typeof(RotationQuaternion)}
};

var query1 = new EntityQueryDesc
{
   All = new ComponentType[] {typeof(RotationSpeed)}
};

EntityQuery m_Group = GetEntityQuery(new EntityQueryDesc[] {query0, query1});
1 Like