Instantiating Prefab Entity On Input

I’ve had a look at the Unity ECS samples, and they show how to create an Entity from a Prefab when the scene is started, using a spawner Entity.

I’m trying to extend the Unity ECS Samples example to also spawn cubes when the mouse is clicked, but I cannot figure out how to do this in an ECS way. I can get to the point where an Input System is able to detect the mouse click and run a Job, but I don’t know how to pass the Prefab Entity to the Job so that the Entity can be created.

Help would be appreciated.

I’m not sure you’ll get any benefit from instantiating a prefab inside a job. If you’re using EntityCommandBuffer, it can’t run in burst and you just create more work with scheduling, caching and processing it later in a different place.

Using EntityManager in Update:

using System.Collections.Generic;
using Unity.Collections;
using Unity.Entities;
using Unity.Transforms;
using UnityEngine;

public class CubeConversion : MonoBehaviour, IDeclareReferencedPrefabs, IConvertGameObjectToEntity
{
    public GameObject CubePrefab;

    public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    {
        dstManager.AddComponentData(entity, new MyPrefabComponent
        {
            Value = conversionSystem.GetPrimaryEntity(CubePrefab)
        });
    }

    public void DeclareReferencedPrefabs(List<GameObject> referencedPrefabs)
    {
        referencedPrefabs.Add(CubePrefab);
    }
}

public class MyNormalSpawnerSystem : ComponentSystem
{
    protected override void OnUpdate()
    {
        var amountToSpawn = 100;
        var cubePrefab = GetSingleton<MyPrefabComponent>();
        var newEntities = new NativeArray<Entity>(amountToSpawn, Allocator.Temp);

        EntityManager.Instantiate(cubePrefab.Value, newEntities);

        for (int i = 0; i < newEntities.Length; i++)
        {
            var spawnPositon = UnityEngine.Random.insideUnitSphere * UnityEngine.Random.value * 10;
            EntityManager.SetComponentData(newEntities[i], new Translation
            {
                Value = spawnPositon
            });
        }

        newEntities.Dispose();
    }
}

public struct MyPrefabComponent : IComponentData
{
    public Entity Value;
}

You could improve that but caching the buffer with a persistent list instead of doing the temp allocation. And maybe update the component data inside a job (with no ECB). Here’s the job instantiate version.

public class MyJobSpawnerSystem : JobComponentSystem
{
    private BeginSimulationEntityCommandBufferSystem _commandsSystem;

    protected override void OnCreate()
    {
        _commandsSystem = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();
    }

    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        inputDeps = new MyInstantiateJob
        {
            Commands = _commandsSystem.CreateCommandBuffer(),
            Prefab = GetSingleton<MyPrefabComponent>(),
            AmountToSpawn = 10,
            Random = new Unity.Mathematics.Random((uint)UnityEngine.Random.Range(1, 100000))

        }.Schedule(inputDeps);

        _commandsSystem.AddJobHandleForProducer(inputDeps);

        return inputDeps;
    }

    public struct MyInstantiateJob : IJob
    {
        public int AmountToSpawn;
        public MyPrefabComponent Prefab;
        public EntityCommandBuffer Commands;
        public Unity.Mathematics.Random Random;

        public void Execute()
        {
            for (int i = 0; i < AmountToSpawn; i++)
            {
                var entity = Commands.Instantiate(Prefab.Value);

                Commands.SetComponent(entity, new Translation
                {
                    Value = Random.NextFloat3Direction() * Random.NextFloat(0, 10)
                });
            }
        }
    }
}
1 Like

Instantiating a prefab using an entity command buffer is definitely burstable; so is destroy entity.

:hushed: My mistake, you’re right. It’s just the Add/Set etc that have an issue with the TypeManager index lookup and so only work if you hack it to pass in the index manually.

Yes. However, I believe they intend to eventually resolve that issue. I don’t think it’s unreasonable to make significant use of the EntityCommandBuffer, now, and hope that it becomes fully burstable further down the line. I do think that batch APIs are, on the whole, the way to go.

1 Like

Thanks for your help. I was unable to get something going with SetSingleton (InvalidOperationException). I was calling this from a MonoBehaviour.Convert script.

I managed to solve my immediate problem by getting a reference to the system in MonoBehaviour.Convert and setting a member directly which contained the Prefab entity. Not scalable, but at-least it works…