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.
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)
});
}
}
}
}
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.
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…