Unity Entities 1.3 — Why is something as simple as prefab instantiation this hard?

Context

I’m trying to make a very simple test project using Unity 6000.0.32 with Entities 1.3.10 and Entities Graphics 1.3.2. The goal? Just spawn a prefab with a custom component at runtime. That’s it.


Repro Steps

  • Create a new Unity project (6000.0.32)
  • Install:
    • Entities 1.3.10
    • Entities Graphics 1.3.2
  • Right-click in the Scene > Create SubScene (Side note: Unity already throws an error: InvalidOperationException: Cannot modify VisualElement hierarchy during layout calculation*… okay then.)*
  • Create a Cube ECS Prefab
    • In the Hierarchy: Create a Cube
    • Drag it into Assets/Prefabs to create a prefab, then delete it from the scene.
    • Create a script at Assets/Scripts/CubeAuthoring.cs:
using UnityEngine;
using Unity.Entities;

public class CubeAuthoring : MonoBehaviour
{
    public float value = 42f;
}

public struct CubeComponent : IComponentData
{
    public float value;
}

public class CubeBaker : Baker<CubeAuthoring>
{
    public override void Bake(CubeAuthoring authoring)
    {
        Entity entity = GetEntity(TransformUsageFlags.Dynamic);
        AddComponent(entity, new CubeComponent { value = authoring.value });
    }
}
  • Attach the CubeAuthoring script to the prefab.
  • Add the prefab to the SubScene.
  • Create the Spawner:
    • Create a new GameObject in the scene and add a MonoBehaviour:
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;
using Random = UnityEngine.Random;

public class CubeSpawner : MonoBehaviour
{
    void Start()
    {
        var world = World.DefaultGameObjectInjectionWorld;
        var entityManager = world.EntityManager;

        var query = entityManager.CreateEntityQuery(
            ComponentType.ReadOnly<CubeComponent>(),
            ComponentType.ReadOnly<Prefab>());

        var prefabs = query.ToEntityArray(Unity.Collections.Allocator.Temp);

        Debug.Log($"[Spawner] Found {prefabs.Length} prefab(s) with CubeComponent and Prefab tag.");

        foreach (var prefab in prefabs)
            for (int i = 0; i < 10; i++)
                Spawn(entityManager, prefab);

        prefabs.Dispose();
    }

    void Spawn(EntityManager entityManager, Entity prefab)
    {
        var instance = entityManager.Instantiate(prefab);
        entityManager.SetComponentData(instance, new LocalTransform
        {
            Position = new float3(Random.Range(-5f, 5f), Random.Range(-5f, 5f), Random.Range(-5f, 5f)),
            Rotation = quaternion.identity,
            Scale = 1f
        });
    }
}

Play the scene.
→ Console output: "[Spawner] Found 0 prefab(s) with CubeComponent and Prefab tag."

Okay… Cube is a `.prefab` but do not get the Component… ?! :skull:

Fix: Add the prefab tag manually in the Cube Baker `AddComponent(entity); `

Play again
→ it works! :tada:

Then… try to Build & Run OR just close the SubScene and play again in Editor
→ Console: "[Spawner] Found 0 prefab(s) with CubeComponent and Prefab tag." :skull:


Another test

Create a new Prefab with a Parent and a Cube: Redo the same step as the first Cube but this time add an Empty Parent around the cube and put the CubeAuthoring on the parent.
Replace the Cube on SubScene by the new Cube Parent.

Play
→ Still doesn’t work ! :skull:

In the Entities Hierarchy (Play Mode), I see the entity named 10 Cube Parent, but it has no children. Though visually, I can see the child cube mesh of the Prefab.:skull: (Not removed on this case ?!)


Conclusion

How is instantiating a prefab — which is supposed to be the foundation of working with thousands of ECS entities — this frustrating and inconsistent?

I’m not doing anything crazy:

  • One component
  • One baker
  • One prefab
  • One spawner

What did I do wrong ?! (I can provide a Minimal reproductible project if someone need it?)

BTW I also tested to:

  • run the Spawn function later (using a Button action) to be sure that the ECS World is well setup and avoid race condition.
  • Or even to replace the CubeSpawner MonoBehavior using a ISystem..

Same results.

This makes baking no longer treat it like a prefab. It is a scene instance again. If you want a true prefab entity, then you should call GetEntity() inside a baker passing a GameObject prefab that you might have serialized in a MonoBehaviour. See EntityComponentSystemSamples/Dots101/Entities101/Assets/HelloCube/4. Prefabs/SpawnerAuthoring.cs at master · Unity-Technologies/EntityComponentSystemSamples · GitHub

Ok so if I try to understand it, to be able to have a “real” ECS prefab you need a container on the SubScene that hold one (or a list) of GameObject reference.

And you Bake the “Container Entity”, and during the baking you use GetEntity and store it somewhere (or it live alone once it as been get ?) then you can use it as a Prefab ?!
No very intuitive but I think I got the point, and I’ll try to implement it.

Also, I read that Unity is implementing ECS Prefab baking without SubScene, is something that exist ? Or not at all ?!

Yeah. This is how it works. It isn’t very intuitive if you try to spawn from a MonoBehaviour. But if you also make your spawner an entity in the subscene, it is actually a smooth workflow.

Don’t expect “without SubScene” tech for a while.

I tested your solution and it worked like a charms.. =]

Thank (Again.. always you on every thread I post/read ^^)