Using IJobChunk for spawning Entities

Hello Everyone,

I am new to DOTS and trying to understand the examples.
I have recently tried to refactor Hello_Cube_07_Fluent_Query’s two systems, the “spawner” script (HelloSpawnMonoBehaviour) and also the “MovementSystem” script in order to use IJobChunk.

When I just refactor the MovementSystem, it works fine even with Burst, but I couldn’t figure out how to refactor to implement IJobChunk in the “spawner” script. I mostly get I cannot use Burst for spawning job.

MovementSystem and SpawnerSystem codes are below. If you can redirect me to the correct implementation, I would be glad.

Thanks in advance.

MovementSystem.cs

using UnityEngine;
using Unity.Entities;
using Samples.HelloCube_07;
using Unity.Collections;
using Unity.Burst;
using Unity.Transforms;
using Unity.Mathematics;
using Unity.Jobs;

[UpdateInGroup(typeof(SimulationSystemGroup))]
[UpdateAfter(typeof(SpawnerSystem))]
public class JobMovementSystem : JobComponentSystem
{
    private EntityQuery selectionGroup;
    private EndInitializationEntityCommandBufferSystem entityCommandBufferSystem;


    protected override void OnCreate()
    {
        EntityManager entityManager = World.Active.EntityManager;
        selectionGroup = GetEntityQuery(typeof(MoveUp), typeof(MovingCube), typeof(Translation));

        entityCommandBufferSystem = World.GetOrCreateSystem<EndInitializationEntityCommandBufferSystem>();
    }

    [BurstCompile]
    private struct MovementJob : IJobChunk
    {
        [ReadOnly]public float DeltaTime;
        public ArchetypeChunkComponentType<Translation> positionType;
        [ReadOnly] public EntityCommandBuffer commandBuffer;

        public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
        {
            var positionChunks = chunk.GetNativeArray(positionType);


            for (var i = 0; i < chunk.Count; i++)
            {
                var position = positionChunks[i];

                if (position.Value.y > 8f)
                {
                    position = new Translation
                    {

                        Value = new float3(position.Value.x, -8f, position.Value.z)
                    };

                }

                    position = new Translation
                {

                    Value = new float3(position.Value.x, position.Value.y + DeltaTime, position.Value.z)
                };


                positionChunks[i] = position;

            }
        }
    }
    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        var positionType = GetArchetypeChunkComponentType<Translation>(false);


        var job = new MovementJob
        {
            positionType = positionType,
            DeltaTime = Time.deltaTime,
            commandBuffer = entityCommandBufferSystem.CreateCommandBuffer()

        };

        return job.Schedule(selectionGroup, inputDeps);


    }
}

SpawnerSystem.cs
```csharp
**using UnityEngine;
using Unity.Entities;
using Samples.HelloCube_07;
using Unity.Collections;
using Unity.Burst;
using Unity.Transforms;
using Unity.Mathematics;
using Unity.Jobs;

[UpdateInGroup(typeof(SimulationSystemGroup))]
[UpdateAfter(typeof(SpawnerSystem))]
public class JobMovementSystem : JobComponentSystem
{
private EntityQuery selectionGroup;

protected override void OnCreate()
{
    EntityManager entityManager = World.Active.EntityManager;
    selectionGroup = GetEntityQuery(typeof(MoveUp), typeof(MovingCube), typeof(Translation));

}

[BurstCompile]
private struct MovementJob : IJobChunk
{
    [ReadOnly]public float DeltaTime;
    public ArchetypeChunkComponentType<Translation> positionType;
   

    public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
    {
        var positionChunks = chunk.GetNativeArray(positionType);


        for (var i = 0; i < chunk.Count; i++)
        {
            var position = positionChunks[i];

            if (position.Value.y > 8f)
            {
                position = new Translation
                {

                    Value = new float3(position.Value.x, -8f, position.Value.z)
                };

            }

                position = new Translation
            {

                Value = new float3(position.Value.x, position.Value.y + DeltaTime, position.Value.z)
            };


            positionChunks[i] = position;

        }
    }
}
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
    var positionType = GetArchetypeChunkComponentType<Translation>(false);


    var job = new MovementJob
    {
        positionType = positionType,
        DeltaTime = Time.deltaTime,

    };

    return job.Schedule(selectionGroup, inputDeps);


}

}**
```

As far as I understand the real problem stems from the triple for loop inside chunk iterator.
How can I solve this issue? First loop is a must for chunk iteration and the other 2 are required for grid instantiation.

If you’re using a command buffer in a parallel job you need to use the EntityCommandBuffer.Concurrent. You can call CreateCommandBuffer().ToConcurrent() when creating it. Then you pass in the chunk index when instantiating/destroying with it.

Make sure you also do commandBufferSystem.AddJobHandleForProducer in OnUpdate with your job after you schedule it.

Followed your suggestion, but unfortunately the problem still continues.
Entities keep being instantiated and the scene gets crowded with them.

private struct SpawnJob : IJobChunk
    {
        [ReadOnly] public ArchetypeChunkComponentType<SpawnerComponent> spawnerType;
        [ReadOnly] public ArchetypeChunkComponentType<LocalToWorld> locationType;
      
        [ReadOnly] public EntityCommandBuffer.Concurrent commandBuffer;

        public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
        {
            NativeArray<SpawnerComponent> spawnerChunks = chunk.GetNativeArray(spawnerType);
            NativeArray<LocalToWorld> locationChunks = chunk.GetNativeArray(locationType);
          
          
            for (var i = 0; i < chunk.Count; i++)
            {
                var position = locationChunks[i].Value;
                var spawner = spawnerChunks[i];
                Entity entity = Entity.Null;

                for (int x = 0; x < spawner.Xaxis ; x++)
                {
                    for (int y = 0; y < spawner.Yaxis; y++)
                    {
                         entity = commandBuffer.Instantiate(chunkIndex, spawner.Cube);

                        // Place the instantiated in a grid with some noise
                        var location = math.transform(position, new float3(x - spawner.Xaxis / 2, noise.cnoise(new float2(x, y) * 0.21F) * 10, y - spawner.Yaxis / 2));

                        commandBuffer.SetComponent(chunkIndex,entity, new Translation { Value = location });

                    }

                }
                commandBuffer.DestroyEntity(chunkIndex, entity);
            }

        }

    }


    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        var locationType = GetArchetypeChunkComponentType<LocalToWorld>(true);
        var spawnerType = GetArchetypeChunkComponentType<SpawnerComponent>(true);
      

        var job = new SpawnJob
        {
            locationType = locationType,
            spawnerType = spawnerType,
            commandBuffer = entityCommandBufferSystem.CreateCommandBuffer().ToConcurrent()
       
        }.Schedule(query, inputDeps);
        entityCommandBufferSystem.AddJobHandleForProducer(job);
        return job;

      
    }

You’re instantiating x * y entities (one for each run of your inner loops) but only calling Destroy once after your loops have already created all the entities. If you want to destroy them all afterwards you need to put them in a list as you make them or create an EntityQuery and call DestroyEntity on the the list or query.