docs.unity3d.com
Search Results for

    Show / Hide Table of Contents

    Entity prefab instantiation workflow

    This workflow demonstrates entity prefab instantiation in ECS. The workflow demonstrates the following concepts:

    • Authoring GameObject component for controlling the instantiation using the Editor.
    • Converting GameObject prefabs into ECS prefabs.
    • Creating a Burst-compatible system.
    • Optimizing a system to run across multiple threads.
    Note

    If you've followed the steps in the Authoring and baking workflow example, skip the first step that creates a subscene and start with Create a spawner entity for instantiating prefabs.

    Topics in this section are workflow steps that depend on previous steps to work. If you are following along in the Editor, follow the steps in order.

    1. Create the subscene for the example
    2. Create a spawner entity for instantiating prefabs
    3. Create a system that instantiates prefabs
    4. Make the Spawner system multi-threaded

    Prerequisites

    This workflow requires a Unity 6 project with the following packages installed:

    • Entities
    • Entities Graphics

    Create the subscene for the example

    The first step in the Entity Component System (ECS) workflow is to create a subscene. ECS uses subscenes instead of scenes to manage the content for your application, because Unity's core scene system is incompatible with ECS.

    To create a subscene in Unity:

    1. In the Editor, open an existing scene.
    2. In the Hierarchy, right-click and select New Sub Scene > Empty Scene.
    3. In the prompt that appears, enter the name for the new subscene and save it. Unity adds the subscene to the open scene and you can now use it.

    Create a spawner entity for instantiating prefabs

    This example creates an authoring GameObject called Spawner to provide a way to control how prefabs are instantiated from the Editor. A baker class passes the data from the Spawner to a corresponding ECS entity.

    1. In the subscene, create a new empty GameObject called Spawner.

    2. Create a C# script called SpawnerAuthoring.cs and replace the contents of the file with the following code:

      using UnityEngine;
      using Unity.Entities;
      using Unity.Mathematics;
      
      class SpawnerAuthoring : MonoBehaviour
      {
          public GameObject Prefab;
          public float SpawnRate;
      }
      
      class SpawnerBaker : Baker<SpawnerAuthoring>
      {
          public override void Bake(SpawnerAuthoring authoring)
          {
              // This line converts the Spawner GameObject into an Entity.
              // TransformUsageFlags is None because the Spawner entity is not
              // rendered and does not need a LocalTransform component.
              var entity = GetEntity(TransformUsageFlags.None);
              AddComponent(entity, new Spawner
              {
                  // This GetEntity call converts a GameObject prefab into an entity
                  // prefab. The prefab is rendered, so it requires the standard Transform
                  // components, that's why TransformUsageFlags is set to Dynamic.
                  Prefab = GetEntity(authoring.Prefab, TransformUsageFlags.Dynamic),
                  SpawnPosition = authoring.transform.position,
                  SpawnRate = authoring.SpawnRate,
                  NextSpawnTime = 0f
              });
          }
      }
      
      public struct Spawner : IComponentData
      {
          public Entity Prefab;
          public float3 SpawnPosition;
          public float SpawnRate;
          // This field is used only for the multi-threading example.
          public float NextSpawnTime;
      }
      

      The spawner entity serves as a configuration object and is not meant to be rendered, so it doesn't need the Transform components. That's why the GetEntity call that converts the Spawner GameObject into an entity has the TransformUsageFlags enum set to None.

      var entity = GetEntity(TransformUsageFlags.None);
      

      The AddComponent method adds the the Spawner component, which includes the Prefab field.

      The following GetEntity call converts a GameObject prefab into an entity prefab. The prefab represents the rendered cubes, so it requires the standard Transform components, that's why the TransformUsageFlags enum set to Dynamic.

      Prefab = GetEntity(authoring.Prefab, TransformUsageFlags.Dynamic)
      
    3. Create a prefab by dragging a cube with the Rotation Speed Authoring component to a folder in the Project window.

    4. Select the Spawner GameObject. In the Spawner Authoring component, in the Prefab field, select the cube prefab.

    The ECS framework converts a GameObject prefab into an entity prefab as soon as you select it in the Prefab field. To observe this behavior, do the following:

    1. Open the Entities Hierarchy window using the menu Window > Entities > Hierarchy.

    2. In the regular Hierarchy window, select the Spawner GameObject.

    3. In the Entities Hierarchy window, switch to the Runtime data mode.

      If the Spawner GameObject has the Cube prefab selected in the Prefab field, the Entities Hierarchy should display a view similar to this:

      The Spawner GameObject in the regular Hierarchy window, and the Spawner entity in Entities Hierarchy.

      The screenshot displays the following:

      • The regular Hierarchy window with the Spawner GameObject.
      • The Inspector window in the Authoring data mode. You can edit the properties on the Spawner GameObject from the Editor.
      • The Entities Hierarchy window in the Runtime data mode. In addition to the Spawner entity, this window displays the Cube entity prefab, which has the blue icon next to it. The GetEntity method call in the code example converts the original GameObject prefab into the entity prefab.

    The next step describes how to create a system that instantiates the entity prefab.

    Create a system that instantiates prefabs

    This section describes how to create a system that instantiates entity prefabs and sets component data on them.

    Create a new C# script called SpawnerSystem.cs and replace the contents of the file with the following code:

    using Unity.Entities;
    using Unity.Transforms;
    using Unity.Burst;
    using Unity.Mathematics;
    
    public partial struct SpawnerSystem : ISystem
    {
        private float nextSpawn;
    
        // The Random struct is from the Unity Mathematics package, which provides types
        // and functions optimized for Burst.
        private Random random;
    
        public void OnCreate(ref SystemState state)
        {
            // This call prevents the system from updating unless at least one entity with
            // the Spawner component exists in the ECS world.
            // This also prevents GetSingleton from throwing an exception if it doesn't find
            // an object of type Spawner.
            state.RequireForUpdate<Spawner>();
    
            random = new Random((uint)System.DateTime.Now.Ticks);
        }
    
        [BurstCompile]
        public void OnUpdate(ref SystemState state)
        {
            // Use the GetSingleton method when there is only one entity of a 
            // specific type in the ECS world.
            Spawner spawner = SystemAPI.GetSingleton<Spawner>();
    
            if (nextSpawn < SystemAPI.Time.ElapsedTime)
            {
                // The Prefab field of the spawner variable contains a reference to 
                // the entity prefab which ECS converts during the baking stage.
                Entity newEntity = state.EntityManager.Instantiate(spawner.Prefab);
    
                float3 randomOffset = (random.NextFloat3() - 0.5f) * 10f;
                randomOffset.y = 0;
    
                float3 newPosition = spawner.SpawnPosition + randomOffset;
    
                state.EntityManager.SetComponentData(newEntity,
                                            LocalTransform.FromPosition(newPosition));
    
                nextSpawn = (float)SystemAPI.Time.ElapsedTime + spawner.SpawnRate;
            }
        }
    }
    

    ECS systems are not attached to specific entities, which means that a system's OnUpdate method might run before Unity loads a scene and initializes an entity that the system depends on. Executing the RequireForUpdate method ensures that the system does not run before an entity of type Spawner is initialized in the world:

    state.RequireForUpdate<Spawner>();
    

    In this example, there is only one spawner entity in the subscene, so instead of using a query you can use the GetSingleton method to get the entity:

    Spawner spawner = SystemAPI.GetSingleton<Spawner>();
    

    The system instantiates entity prefabs using the EntityManager.Instantiate method. The Prefab field of the spawner variable contains a reference to the entity prefab which ECS converts on the baking stage:

    Entity newEntity = state.EntityManager.Instantiate(spawner.Prefab);
    

    To avoid instantiating entities in the same location, the example uses the SetComponentData method to set the LocalTransform values on each new entity to a random position within a small vicinity from the spawner position:

    state.EntityManager.SetComponentData(newEntity, LocalTransform.FromPosition(newPosition));
    

    The Random method in the example is from the Unity Mathematics package. The Unity Mathematics package provides types and functions optimized for Burst.

    Try the system in action

    Enter Play mode. The SpawnerSystem system starts creating instances of entity prefabs at the rate specified in the Spawn Rate property of the Spawner GameObject.

    If you followed the instructions in the Authoring and baking workflow example and your project has the RotationSystem.cs script, the prefabs should spin in the Game view.

    Pause Play mode. Open the Entities Hierarchy window and switch to the Runtime data mode.

    The Entities Hierarchy window displays the instantiated entity prefabs.

    The window highlights the source entity prefab with the solid blue icon, and the instantiated entity prefabs with hollow grey icons and blue names.

    Select the source entity prefab and view it in the Inspector window in the Runtime data mode. Notice that it has the Prefab tag in the Tags section. This tag excludes the source prefab from system queries that affect the instances of the prefab.

    Make the Spawner system multi-threaded

    This task shows you how to modify a system so that it runs jobs 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.

    Update the Spawner system

    1. Open the SpawnerSystem.cs script.
    2. Replace the contents of the file with the below code example.
    3. Duplicate the Spawner GameObject in the subscene so that you have at least 300 Spawner GameObjects. This is to ensure that there are enough entities for ECS to split the work among multiple threads.
    4. 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.
    using Unity.Entities;
    using Unity.Transforms;
    using Unity.Burst;
    using Unity.Mathematics;
    
    [BurstCompile]
    public partial struct SpawnerSystemMultithreaded : ISystem
    {
        private Random random;
    
        public void OnCreate(ref SystemState state)
        {
            state.RequireForUpdate<Spawner>();
    
            random = new Random((uint)System.DateTime.Now.Ticks);
        }
    
        [BurstCompile]
        public void OnUpdate(ref SystemState state)
        {
            // Initialize an EntityCommandBuffer to record entity operations from
            // multiple threads. You can only create entities on the main thread.
            // Here, we record entity operations in an entity command buffer during
            // parallel job execution and play them back on the main thread to create
            // and configure the entities.
            EntityCommandBuffer.ParallelWriter ecb = GetEntityCommandBuffer(ref state);
    
            // Creates a new instance of the job, assigns the necessary data, and
            // schedules the job to run in parallel.
            new ProcessSpawnerJob
            {
                ElapsedTime = SystemAPI.Time.ElapsedTime,
                Ecb = ecb,
                RandomSeed = random.NextUInt()
            }.ScheduleParallel();
    
            // Update the random seed for next frame
            random.InitState(random.NextUInt());
        }
    
        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;
        public uint RandomSeed;
    
        // IJobEntity generates a component data query based on the parameters of its
        // Execute method. This example queries for Spawner components and uses the ref
        // keyword to specify that the operation requires read and write access. Unity
        // runs the code in the Execute method for each entity that matches the query.
        private void Execute([ChunkIndexInQuery] int chunkIndex, ref Spawner spawner)
        {
            // If the next spawn time has passed.
            if (spawner.NextSpawnTime < ElapsedTime)
            {
                // Create a deterministic random generator for this entity
                var randomGenerator = new Random(RandomSeed + (uint)chunkIndex);
    
                // Spawns a new entity and positions it at the spawner.
                Entity newEntity = Ecb.Instantiate(chunkIndex, spawner.Prefab);
    
                // Calculate random offset
                float3 randomOffset = (randomGenerator.NextFloat3() - 0.5f) * 10f;
                randomOffset.y = 0;
    
                float3 newPosition = spawner.SpawnPosition + randomOffset;
    
                Ecb.SetComponent(chunkIndex, newEntity,
                                            LocalTransform.FromPosition(newPosition));
    
                // Resets the next spawn time.
                spawner.NextSpawnTime = (float)ElapsedTime + spawner.SpawnRate;
            }
        }
    }
    

    Note that the BeginSimulationEntityCommandBufferSystem is used to create the entity command buffer. This is a system which runs at the start of the SimulationSystemGroup, and it ensures that the entity command buffer is played back at the beginning of the Update phase of the player loop. For more information on default system groups see System groups.

    Additional resources

    • Introduction to the ECS workflow
    • Starter ECS workflow
    • Authoring and baking workflow example
    In This Article
    Back to top
    Copyright © 2025 Unity Technologies — Trademarks and terms of use
    • Legal
    • Privacy Policy
    • Cookie Policy
    • Do Not Sell or Share My Personal Information
    • Your Privacy Choices (Cookie Settings)