trying to pick up ECS. I am having a real hard time with instantiating & destroy entities at the same job.
in this example, i have a lot of drones on screen. they will move towards a destination trivially(no collision/path find). once at the destination, a new random destination gets assigned, and the item the drone carries changes. the item being carried is a simple prefab, child under a transform under the prefab.
so i can get the object to instantiate just fine, but i couldn’t figure out how to delete the old item when a change of item happens. here are the core code:
// in create
var ecbSingleton = SystemAPI.GetSingleton<BeginSimulationEntityCommandBufferSystem.Singleton>();
var ecb = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged);
var stateUpdateJob = new StateUpdateJob
{
randomArray = randomArray,
ecb = ecb.AsParallelWriter(),
itemDisplayBuffer = state.GetBufferLookup<ItemDisplayPrefabElement>(true),
spawnerEntity = beltDroneSpawnerEntity
};
state.Dependency = stateUpdateJob.ScheduleParallel(state.Dependency);
// in job
if (carryItem.item != Entity.Null) {
ecb.DestroyEntity(entityInQueryIndex, carryItem.item);
carryItem.item = Entity.Null;
}
var itemDisplayBufferEntity = spawnerEntity;
var prefabsBuffer = itemDisplayBuffer[itemDisplayBufferEntity];
var newItemPrefab = prefabsBuffer[(int)droneState.itemType].PrefabEntity;
if (newItemPrefab != Entity.Null) {
// Instantiate the new item and assign it to carryItem.item
var newItem = ecb.Instantiate(entityInQueryIndex, newItemPrefab);
ecb.AddComponent<Parent>(entityInQueryIndex, newItem, new Parent {Value = carryItem.container});
carryItem.item = newItem;
}
this is my code:
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
[BurstCompile]
public partial struct SimpleMoverSystem : ISystem {
private NativeArray<Unity.Mathematics.Random> randomArray;
ComponentLookup<PreviousPosition> previousPositionLookup;
[BurstCompile]
public void OnCreate(ref SystemState state)
{
previousPositionLookup = state.GetComponentLookup<PreviousPosition>(true);
// Create a random array for each thread in parallel jobs
int workerCount = Unity.Jobs.LowLevel.Unsafe.JobsUtility.MaxJobThreadCount;
randomArray = new NativeArray<Unity.Mathematics.Random>(workerCount, Allocator.Persistent);
uint seed = (uint)UnityEngine.Random.Range(1, uint.MaxValue);
for (int i = 0; i < workerCount; i++)
{
randomArray[i] = new Unity.Mathematics.Random(seed + (uint)i);
}
state.RequireForUpdate<BeginSimulationEntityCommandBufferSystem.Singleton>();
}
[BurstCompile]
public void OnDestroy(ref SystemState state)
{
if (randomArray.IsCreated)
{
randomArray.Dispose();
}
}
[BurstCompile]
public void OnUpdate(ref SystemState state) {
previousPositionLookup.Update(ref state);
float deltaTime = SystemAPI.Time.DeltaTime;
NativeArray<Unity.Mathematics.Random> randoms = randomArray;
var simpleMoveJob = new SimpleMove
{
deltaTime = deltaTime,
};
var handle = simpleMoveJob.ScheduleParallel(state.Dependency);
state.Dependency = handle; // Correctly assign the dependency
var beltMoveJob = new BeltMove
{
deltaTime = deltaTime,
previousPositionFromEntity = previousPositionLookup
};
handle = beltMoveJob.ScheduleParallel(handle);
state.Dependency = handle;
var beltDroneSpawnerEntity = SystemAPI.GetSingletonEntity<BeltDroneSpawner>();
// var ecb = new EntityCommandBuffer(Allocator.TempJob);
var ecbSingleton = SystemAPI.GetSingleton<BeginSimulationEntityCommandBufferSystem.Singleton>();
var ecb = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged);
var stateUpdateJob = new StateUpdateJob
{
randomArray = randomArray,
ecb = ecb.AsParallelWriter(),
itemDisplayBuffer = state.GetBufferLookup<ItemDisplayPrefabElement>(true),
spawnerEntity = beltDroneSpawnerEntity
};
state.Dependency = stateUpdateJob.ScheduleParallel(state.Dependency);
}
}
// [BurstCompile]
public partial struct StateUpdateJob : IJobEntity {
[NativeDisableParallelForRestriction]
public NativeArray<Unity.Mathematics.Random> randomArray;
public EntityCommandBuffer.ParallelWriter ecb; // To queue up entity commands
[ReadOnly] public BufferLookup<ItemDisplayPrefabElement> itemDisplayBuffer; // BufferLookup instead of BufferFromEntity
[ReadOnly] public Entity spawnerEntity;
public void Execute(in LocalTransform transform, ref SimpleMover simpleMover, ref DroneState droneState, ref CarryItem carryItem, [EntityIndexInQuery] int entityInQueryIndex) {
if (math.lengthsq(transform.Position - simpleMover.destination) < 0.0001f) {
// Use thread-specific Random object
var random = randomArray[entityInQueryIndex % randomArray.Length];
simpleMover.destination = random.NextFloat3(new float3(-50f, 0, -50f), new float3(50f, 0, 50f));
var oldItemType = droneState.itemType;
droneState.itemType = (DOTS_ItemType)random.NextInt(0, (int)DOTS_ItemType.LastValue + 1);
if (oldItemType != droneState.itemType) {
// delete entity carryItem.item
if (carryItem.item != Entity.Null) {
ecb.DestroyEntity(entityInQueryIndex, carryItem.item);
carryItem.item = Entity.Null;
}
var itemDisplayBufferEntity = spawnerEntity;
var prefabsBuffer = itemDisplayBuffer[itemDisplayBufferEntity];
var newItemPrefab = prefabsBuffer[(int)droneState.itemType].PrefabEntity;
if (newItemPrefab != Entity.Null) {
// Instantiate the new item and assign it to carryItem.item
var newItem = ecb.Instantiate(entityInQueryIndex, newItemPrefab);
ecb.AddComponent<Parent>(entityInQueryIndex, newItem, new Parent {Value = carryItem.container});
carryItem.item = newItem;
}
}
// Update the Random in the array after use
randomArray[entityInQueryIndex % randomArray.Length] = random;
}
}
}
[BurstCompile]
public partial struct SimpleMove : IJobEntity
{
public float deltaTime;
public void Execute(ref LocalTransform transform, ref SimpleMover simpleMover, ref PreviousPosition previousPosition, [EntityIndexInQuery] int entityInQueryIndex)
{
var position = transform.Position;
var destination = simpleMover.destination;
var speed = simpleMover.speed;
var diff = destination - position;
var distanceSq = math.lengthsq(diff);
var travelDistance = speed * deltaTime;
if (distanceSq < travelDistance * travelDistance)
{
transform.Position = destination;
}
else
{
var direction = math.normalize(diff);
var newPosition = position + direction * travelDistance;
transform.Position = newPosition;
}
previousPosition.delta = transform.Position - position;
}
}
[BurstCompile]
public partial struct BeltMove : IJobEntity {
public float deltaTime;
[ReadOnly] // Mark this as ReadOnly as well
public ComponentLookup<PreviousPosition> previousPositionFromEntity;
public void Execute(in BeltMover beltMover, ref LocalTransform transform, in Parent parent) {
// Retrieve LocalTransform and PreviousPosition from parent entity
var parentPreviousPosition = previousPositionFromEntity[parent.Value];
var flatDelta = new float3(parentPreviousPosition.delta.x, 0, parentPreviousPosition.delta.z);
var distanceSq = math.lengthsq(flatDelta);
if (distanceSq > 0) {
// Turn towards the direction we are moving
var beltFlatDir = math.normalize(flatDelta);
var beltForwardFlat = transform.Forward();
beltForwardFlat.y = 0;
beltForwardFlat = math.normalize(beltForwardFlat);
// Calculate the angle difference in radians
// Calculate the angle difference between the current forward direction and the target direction
var up = new float3(0, 1, 0); // Rotate around the Y-axis
float angleDifference = CFNUtils.SignedAngle(beltForwardFlat, beltFlatDir, up);
// Determine if the belt should rotate towards the target or reverse
float rotationAngle = angleDifference > 90 ? angleDifference - 180 : angleDifference < -90 ? angleDifference + 180 : angleDifference;
// Apply the rotation towards the target
var rotationStep = math.mul(transform.Rotation, quaternion.AxisAngle(up, rotationAngle * deltaTime * beltMover.speed * math.TORADIANS));
transform.Rotation = rotationStep;
}
}
}
public partial struct PreviousPosition : IComponentData {
public float3 delta;
}
public partial struct SimpleMover : IComponentData {
public float speed;
public float3 destination;
}
public partial struct BeltMover : IComponentData {
public float speed;
}
public partial struct DroneState : IComponentData {
public DOTS_ItemType itemType;
}```