docs.unity3d.com
Search Results for

    Show / Hide Table of Contents

    Scene instancing

    To create multiple instances of the same scene in a world, use SceneSystem.LoadSceneAsync with the flag SceneLoadFlags.NewInstance. This is useful if for example you have a different tiles (each tile represented by a scene) and you want to populate the world with those tiles.

    When you create a scene in this way, the scene meta entity returned from the load call will refer to the newly created instance.

    The instances from a scene are exact copies of each other, because the streaming system loads the exact same data multiple times from the entity scene file. To make sure that each instance isn't exactly the same, you can apply a unique transform on each instance by combining the ProcessAfterLoadGroup system group with the RequestSceneLoaded.ImportEntity field. You can apply any other kind of changes to the entities in the scene, not just a transform.

    Note

    Any custom section metadata is exactly the same on each instance because the meta data is stored in the entity scene file.

    ProcessAfterLoadGroup system group

    The loading of a section doesn't happen in the main world, but on a separate world called the streaming world. Each section loads into its own streaming world. When the load is complete, the content of the streaming world is moved into the main world.

    The system group ProcessAfterLoadGroup runs in the streaming world when all the content is loaded, but before the final move into the main world is performed. You can add custom systems into that group to apply transformations to scene instances.

    For example, you could create a system to offset all the entities in the instance to a certain position of the world. In this case you need to pass to the system the offset that you want to apply to the instance. This offset can't be stored inside the entity scene file because it needs to be different for each instance. You can use RequestSceneLoaded.ImportEntity to deliver this data into the streaming world.

    RequestSceneLoaded.ImportEntity

    RequestSceneLoaded exposes an ImportEntity field referencing a regular entity in the main world that holds the per-instance data you want to deliver into the streaming world. During section loading, the streaming system copies that referenced entity (and all of its components) into the section's streaming world just before ProcessAfterLoadGroup runs. Your [WorldSystemFilter(WorldSystemFilterFlags.ProcessAfterLoad)] systems can then query for the imported components as if they were native to the streaming world.

    The most convenient way to set ImportEntity is to pass it inline when you start the load, through SceneSystem.LoadParameters.ImportEntity. The streaming system then writes the same value into RequestSceneLoaded on the scene meta entity (and propagates it to each section meta entity at auto-load time). You can also write RequestSceneLoaded directly on a section meta entity if you want a per-section value that overrides the scene-level one.

    The imported entity follows the same lifetime rules as any other entity in the streaming world. If your ProcessAfterLoad system consumes it as a one-shot data carrier and you don't want it to leak into the main world, destroy it inside that system after reading. Otherwise, it receives a SceneTag and is moved into the main world together with the rest of the section's entities, where it persists until you destroy it (or until the section is unloaded).

    You own the source data entity in the main world. Destroy it yourself when it is no longer needed, but keep it alive until the scene load completes. The streaming system only reads from it. If ImportEntity is Entity.Null, no import is performed. A non-null reference to an entity that does not exist (because it was never created, or because it was destroyed before the scene finished loading) is treated as a programming error: the import is skipped and an error is logged.

    Note

    The previous API, the PostLoadCommandBuffer managed IComponentData, is deprecated. Prefer RequestSceneLoaded.ImportEntity in new code. To migrate an existing call site: build the entity in the main world directly with the components that the old EntityCommandBuffer was creating, then pass it as ImportEntity in the LoadParameters you give to SceneSystem.LoadSceneAsync.

    Scene instancing overview

    As a summary, these are the steps to instantiate scenes and apply unique transformations to them:

    1. Build the per-instance data:
      1. Create a regular entity in the main world.
      2. Add components to it carrying the unique instance information.
    2. Use SceneSystem.LoadSceneAsync with the flag SceneLoadFlags.NewInstance to load a scene, passing your data entity as ImportEntity in the LoadParameters.
    3. Write a system to apply the unique transformation to the instanced scene:
      1. Create the system and assign it to the ProcessAfterLoadGroup.
      2. Query the instance information from the imported entity (its components appear in the streaming world).
      3. Use that information to apply the transforms to the entities in the instance.

    For example, to instantiate a scene at a certain position in the world you can do the following:

    // Build a data entity in the main world carrying the per-instance offset.
    var dataEntity = state.EntityManager.CreateEntity();
    state.EntityManager.AddComponentData(dataEntity, new PostLoadOffset
    {
        Offset = sceneOffset
    });
    
    // LoadParameters.ImportEntity directs the streaming system to copy `dataEntity`
    // (and all its components) into the section's loading world before
    // ProcessAfterLoadGroup runs, where PostprocessSystem reads PostLoadOffset and
    // applies it. PostprocessSystem destroys that streaming-world copy so it doesn't
    // get merged back into the main world after the load. The original `dataEntity`
    // created above stays in the main world and is yours to destroy once the load
    // has completed and you no longer need it.
    var sceneEntity = SceneSystem.LoadSceneAsync(state.WorldUnmanaged, sceneReference, new SceneSystem.LoadParameters
    {
        Flags = SceneLoadFlags.NewInstance,
        ImportEntity = dataEntity,
    });
    

    The code above uses a component called PostLoadOffset to store the offset to apply to the instance.

    public struct PostLoadOffset : IComponentData
    {
        public float3 Offset;
    }
    

    Finally, use this system to apply the transformation:

    
    [WorldSystemFilter(WorldSystemFilterFlags.ProcessAfterLoad)]
    public partial struct PostprocessSystem : ISystem
    {
        private EntityQuery offsetQuery;
    
        public void OnCreate(ref SystemState state)
        {
            offsetQuery = new EntityQueryBuilder(Allocator.Temp)
                .WithAll<PostLoadOffset>()
                .Build(ref state);
            state.RequireForUpdate(offsetQuery);
        }
    
        public void OnUpdate(ref SystemState state)
        {
            // Query the instance information from the imported entity, copied into this
            // streaming world by RequestSceneLoaded.ImportEntity.
            var offsets = offsetQuery.ToComponentDataArray<PostLoadOffset>(Allocator.Temp);
            foreach (var offset in offsets)
            {
                // Use that information to apply the transforms to the entities in the instance.
                foreach (var transform in SystemAPI.Query<RefRW<LocalTransform>>())
                {
                    transform.ValueRW.Position += offset.Offset;
                }
            }
            state.EntityManager.DestroyEntity(offsetQuery);
        }
    }
    

    Addional resources

    • Custom section metadata
    • Scene sections
    In This Article
    Back to top
    Copyright © 2026 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)