Hi all, any help with the following would be appreciated. The code below runs fine to find the nearest target to assign to entities, but when the number of entities exceeds the number of targets I get the error
"
System.IndexOutOfRangeException: Index {0} is out of restricted IJobParallelFor range [{1}…{2}] in ReadWriteBuffer.
Thrown from job: FindNearestTargetSystem.<>c__DisplayClass_Compare_Agent_And_Target_Positions
"
From what I have googled it seems like
- it may be fixed by making some element ReadOnly but I haven’t been able to figure it out
- some misunderstanding about how ScheduleParallel works
- some error in my logic
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
public class FindNearestTargetSystem : SystemBase
{
private EntityCommandBufferSystem _ecbSystem;
private EntityQuery _targetsQuery;
private EntityQuery _agentsQuery;
protected override void OnCreate()
{
base.OnCreate();
_ecbSystem = World.GetExistingSystem<EndSimulationEntityCommandBufferSystem>();
}
protected override void OnUpdate()
{
var buffer = _ecbSystem.CreateCommandBuffer().AsParallelWriter();
// make a native array - its length is decided by how many agents we have in our entity query
var agentsQueryCount = _agentsQuery.CalculateEntityCount();
var targetEntities = new NativeArray<Entity>(agentsQueryCount, Allocator.TempJob);
var targetPositions = new NativeArray<float3>(agentsQueryCount, Allocator.TempJob);
// used to temporarily store the distance between an agent and possible targets
var distances = new NativeArray<float>(agentsQueryCount, Allocator.TempJob);
var agentPositions = new NativeArray<float3>(agentsQueryCount, Allocator.TempJob);
// get all AGENT positions
// set a default high/infinity value for distance check
Entities
.WithName("Get_Agent_Positions")
.WithStoreEntityQueryInField(ref _agentsQuery)
.WithAll<TargetData>()
.WithAll<NeedsTargetTag>()
.ForEach((Entity e, int entityInQueryIndex, in Translation translation) =>
{
agentPositions[entityInQueryIndex] = translation.Value;
distances[entityInQueryIndex] = float.PositiveInfinity;
})
.ScheduleParallel();
// nested For loops to find the closest target
// compare all target positions with all agent positions to find the shortest distances for each pair
// TODO - Performance can be enhanced by use of grid check for large maps
Entities
.WithName("Compare_Agent_And_Target_Positions")
.WithAll<TargetTag>()
.WithStoreEntityQueryInField(ref _targetsQuery)
.WithReadOnly(agentPositions)
.ForEach((Entity targetEntity, int entityInQueryIndex, in Translation targetPos) =>
{
// compare all target pos to all agent pos
for (var i = 0; i < agentPositions.Length; i++)
{
// calculate the distance between target and agent
float distance = distances[i];
var newDistance = math.distance(targetPos.Value, agentPositions[i]);
// if the distance is smaller then store this and the associated target and target position
if (newDistance < distance)
{
distances[i] = newDistance;
targetEntities[i] = targetEntity;
targetPositions[i] = targetPos.Value;
}
}
})
.WithDisposeOnCompletion(distances)
.WithDisposeOnCompletion(agentPositions)
.ScheduleParallel();
// assign the data values obtained from the above For loops
Entities
.WithName("Assign_Closest_Target_Position")
.ForEach((Entity e, int entityInQueryIndex, ref TargetData targetData, ref NeedsTargetTag needsTargetTag) =>
{
targetData.TargetPosition = targetPositions[entityInQueryIndex];
targetData.Target = targetEntities[entityInQueryIndex];
buffer.RemoveComponent<NeedsTargetTag>(entityInQueryIndex, e);
})
.WithDisposeOnCompletion(targetEntities)
.WithDisposeOnCompletion(targetPositions)
.ScheduleParallel();
_ecbSystem.AddJobHandleForProducer(Dependency);
}
}