Hi team,
I have 200+ agents at a time, 100 per each time. Each agent needs a list of the ten closest enemies to it. I figured that a job that iterated through a loop of all agents, filter them, and take a Vector3 distance would work. I believe it is working, but as per my profiler, it’s taking 340ms on each 6 of my threads. It bottlenecks when I do this, and I think I’m taking 4 frames to do this.
Can anyone provide feedback to me on my code? It’s my first one and I THINK I have it working?
public class tickManager : MonoBehaviour
{
public static tickManager instance;
private List<Controller> agentsSearchingForTargetsThisFrame = new List<Controller>();
private List<structTargettingJob> listStructTargettingJob = new List<structTargettingJob>();
structTargettingJob tempTargettingJob = new structTargettingJob();
private int counter;
private int totalCount;
//Arrays for the Targetting Job
public float[] arrayTargettingRadius;
public enumTeams[] arrayTeam;
public Vector3[] arrayTargetterPosition;
public int[] arrayPotentialTargetsIndex;
public Vector3[] arrayGameObjectPositions;
public enumTeams[] arrayTeamsOfPotentialTargets;
JobHandle jobHandleTargetting;
private bool targettingJobRunning = false;
private int targettingJobFrameCounter;
void Start()
{
initSingleton();
initTargettingJobArrays();
}
private void initTargettingJobArrays()
{
totalCount = battleManager.instance.arrayAllTroopsInBattle.Length;
arrayTargettingRadius = new float[totalCount];
arrayTeam = new enumTeams[totalCount];
arrayTargetterPosition = new Vector3[totalCount];
arrayPotentialTargetsIndex = new int[totalCount];
arrayGameObjectPositions = new Vector3[totalCount];
}
private void Update()
{
//Targetting - if job isn't running, increment timers and add people for new job every x seconds.
if (!targettingJobRunning)
{
addTargetScansForThisFrameAndPrepJob();
targetScanThisFrame();
removeTargetScansForThisFrame();
}
else //Since the job is running, max it out at 4 frames since using tempjob allocation
{
targettingJobFrameCounter++;
if(targettingJobFrameCounter >=4)
{
jobHandleTargetting.Complete();
targettingJobRunning = false;
targettingJobFrameCounter = 0;
//Now to assign them to each Controller
for(int i = 0; i < totalCount; i++)
{
//go through each game object and update their own arrays of ten closest enemies, etc. TBD.
}
}
}
}
//struct used in the targetting job
struct jobStructIndexToCalculatedTargets
{
public int TargetterUnitIndex;
public NativeArray<(float, int)> ArrayCalculatedTargets;// unit, list of distances and other units
}
struct structTargettingJob : IJobParallelFor
{
[DeallocateOnJobCompletion]
[ReadOnly] public NativeArray<float> jobArrayTargettingRadius;
[DeallocateOnJobCompletion]
[ReadOnly] public NativeArray<enumTeams> jobArrayTeam;
[DeallocateOnJobCompletion]
[ReadOnly] public NativeArray<Vector3> jobArrayTargetterPosition;
[DeallocateOnJobCompletion]
[ReadOnly] public NativeArray<int> jobArrayPotentialTargetsIndex;
[DeallocateOnJobCompletion]
[ReadOnly] public NativeArray<Vector3> jobArrayGameObjectPositions;
[DeallocateOnJobCompletion]
[ReadOnly] public NativeArray<enumTeams> jobArrayTeamsOfPotentialTargets;
public jobStructIndexToCalculatedTargets structTargetterToTargets;
public float tempDistance;
public int counterCalculatedTargets;
public void Execute(int i)
{
//Goes through everything and builds the index arrays of distances to each object by index, within range. Unsorted.
for(int j = 0; j < jobArrayPotentialTargetsIndex.Length; j++)
{
//Inner loop
counterCalculatedTargets = 0;
for (int k = 0; k < jobArrayPotentialTargetsIndex.Length; k++) //J is 'me', K is 'others'
{
//If this is me or my team, skip
if (jobArrayPotentialTargetsIndex[j] == k || jobArrayTeamsOfPotentialTargets[j] == jobArrayTeamsOfPotentialTargets[k]) continue;
tempDistance = Vector3.Distance(jobArrayGameObjectPositions[k], jobArrayTargetterPosition[j]);
//If outside my targetting range? skip
if (tempDistance > jobArrayTargettingRadius[j]) continue;
//Make a list of closest
structTargetterToTargets.TargetterUnitIndex = j;
structTargetterToTargets.ArrayCalculatedTargets[counterCalculatedTargets] = (tempDistance, k);
counterCalculatedTargets++;
}
//Assign values to the J index of closest targets in range, and sort
structTargetterToTargets.ArrayCalculatedTargets.Sort();
}
}
}
private void addTargetScansForThisFrameAndPrepJob()
{
//Debug.Log("entering addTargetSCansForThisFrame");
counter = 0;
listStructTargettingJob.Clear();
//Go through all living or dead agents in the scene
for (int i = 0; i < totalCount; i++)
{
//if dead skip
if (battleManager.instance.arrayAllTroopsInBattle[i].healthstruct.health <= 0)
continue;
//increment timer
battleManager.instance.arrayAllTroopsInBattle[i].targetting.timeSinceLastTargettingScan += Time.deltaTime;
//Build the arrays of information that I will pass into the Job
if(battleManager.instance.arrayAllTroopsInBattle[i].targetting.timeSinceLastTargettingScan >= battleManager.instance.arrayAllTroopsInBattle[i].targetting.targettingRate)
{
arrayTargettingRadius[counter] = battleManager.instance.arrayAllTroopsInBattle[counter].targetting.maxTargetDistance;
arrayTeam[counter] = battleManager.instance.arrayAllTroopsInBattle[counter].team;
arrayTargetterPosition[counter] = battleManager.instance.arrayAllTroopsInBattle[counter].transform.root.position;
arrayPotentialTargetsIndex[counter] = counter;
agentsSearchingForTargetsThisFrame.Add(battleManager.instance.arrayAllTroopsInBattle[i]);
battleManager.instance.arrayAllTroopsInBattle[i].targetting.timeSinceLastTargettingScan = 0;
counter++;
}
}
//Build position array out of loops
for (int j = 0; j < counter; j++)
{
arrayGameObjectPositions[j] = battleManager.instance.arrayAllTroopsInBattle[j].transform.root.position;
}
}
private void removeTargetScansForThisFrame()
{
agentsSearchingForTargetsThisFrame.Clear();
}
private void targetScanThisFrame()
{
//CREATE JOB
if(agentsSearchingForTargetsThisFrame.Count > 0)
{
Debug.Log("PING!: " + agentsSearchingForTargetsThisFrame.Count);
scheduleJobFindNearestTargets();
}
}
private void scheduleJobFindNearestTargets()
{
//Initialize the job data
tempTargettingJob = new structTargettingJob()
{
jobArrayTargettingRadius = new NativeArray<float>(totalCount, Allocator.TempJob),
jobArrayTeam = new NativeArray<enumTeams>(totalCount, Allocator.TempJob),
jobArrayTargetterPosition = new NativeArray<Vector3>(totalCount,Allocator.TempJob),
jobArrayPotentialTargetsIndex = new NativeArray<int>(totalCount,Allocator.TempJob),
jobArrayGameObjectPositions = new NativeArray<Vector3>(totalCount, Allocator.TempJob),
jobArrayTeamsOfPotentialTargets = new NativeArray<enumTeams>(totalCount, Allocator.TempJob),
structTargetterToTargets = new jobStructIndexToCalculatedTargets()
{
TargetterUnitIndex = 0,
ArrayCalculatedTargets = new NativeArray<(float, int)>(totalCount, Allocator.TempJob),
}
};
//Build the arrays for the job
for (int i = 0; i > agentsSearchingForTargetsThisFrame.Count; i++) {
tempTargettingJob.jobArrayTargettingRadius[i] = arrayTargettingRadius[i];
tempTargettingJob.jobArrayTeam[i] = arrayTeam[i];
tempTargettingJob.jobArrayTargetterPosition[i] = arrayTargetterPosition[i];
tempTargettingJob.jobArrayPotentialTargetsIndex[i] = arrayPotentialTargetsIndex[i];
tempTargettingJob.jobArrayGameObjectPositions[i] = arrayGameObjectPositions[i];
tempTargettingJob.jobArrayTeamsOfPotentialTargets[i] = arrayTeamsOfPotentialTargets[i];
}
jobHandleTargetting= new JobHandle();
jobHandleTargetting = tempTargettingJob.Schedule(agentsSearchingForTargetsThisFrame.Count, 1);
targettingJobRunning = true;
}
private void initSingleton()
{
if (instance == null)
{
instance = this;
}
else if (instance != this)
{
Destroy(this);
}
DontDestroyOnLoad(this);
}
}