Optimize the system for the spawner example
This task shows you how to modify a system so that it uses Burst-compatible jobs that run in parallel on multiple threads.
Note
Before you modify a system to run in parallel on multiple threads, consider whether your system affects data on enough entities to make the benefits of multi-threading exceed the overhead of scheduling the jobs. For more information, refer to Optimize systems.
This task recreates SpawnerSystem
using IJobEntity and schedules the job to run in parallel across multiple threads. Using an IJobEntity
changes how you query and iterate over component data, and changes how you instantiate new entities. For information on component data query and iteration changes due to IJobEntity
, refer to Specify a query.
Unity can only create entities on the main thread which means parallel jobs must use an entity command buffer to record commands to create and configure new entities. After the parallel job runs, Unity plays back the entity command buffer on the main thread to actually create and configure the entities. For more information, refer to Use EntityCommandBuffer in a parallel job and Deterministic playback.
ECS workflow overview
This task is the fifth task in a series of five tasks that show you how to create and optimize behavior in an ECS system. At the end of the tasks, you will have a spawner system that reads and writes component data, and instantiates entities. This workflow contains the following tasks:
- Create the subscene for the spawner example
- Create a component for the spawner example
- Create the spawner entity for the spawner example
- Create the system for the spawner example
- Optimize the system for the spawner example
Each task is a prerequisite for the subsequent tasks.
Optimize the spawner system
- Open
SpawnerSystem
. - Replace the contents of the file with the below code example.
- Enter Play mode. You should see that the system behaves as it did previously. However, if you open the Profiler window, you should see that the work runs on multiple threads. Note: To see the result of multi-threading more clearly, duplicate the Spawner in the subscene so that there are multiple spawner components for the system to process.
using Unity.Collections;
using Unity.Entities;
using Unity.Transforms;
using Unity.Burst;
[BurstCompile]
public partial struct OptimizedSpawnerSystem : ISystem
{
public void OnCreate(ref SystemState state) { }
public void OnDestroy(ref SystemState state) { }
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
EntityCommandBuffer.ParallelWriter ecb = GetEntityCommandBuffer(ref state);
// Creates a new instance of the job, assigns the necessary data, and schedules the job in parallel.
new ProcessSpawnerJob
{
ElapsedTime = SystemAPI.Time.ElapsedTime,
Ecb = ecb
}.ScheduleParallel();
}
private EntityCommandBuffer.ParallelWriter GetEntityCommandBuffer(ref SystemState state)
{
var ecbSingleton = SystemAPI.GetSingleton<BeginSimulationEntityCommandBufferSystem.Singleton>();
var ecb = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged);
return ecb.AsParallelWriter();
}
}
[BurstCompile]
public partial struct ProcessSpawnerJob : IJobEntity
{
public EntityCommandBuffer.ParallelWriter Ecb;
public double ElapsedTime;
// IJobEntity generates a component data query based on the parameters of its `Execute` method.
// This example queries for all Spawner components and uses `ref` to specify that the operation
// requires read and write access. Unity processes `Execute` for each entity that matches the
// component data query.
private void Execute([ChunkIndexInQuery] int chunkIndex, ref Spawner spawner)
{
// If the next spawn time has passed.
if (spawner.NextSpawnTime < ElapsedTime)
{
// Spawns a new entity and positions it at the spawner.
Entity newEntity = Ecb.Instantiate(chunkIndex, spawner.Prefab);
Ecb.SetComponent(chunkIndex, newEntity, LocalTransform.FromPosition(spawner.SpawnPosition));
// Resets the next spawn time.
spawner.NextSpawnTime = (float)ElapsedTime + spawner.SpawnRate;
}
}
}