Hey!
I know it’s been a while, but i got it soved myself.
The first is the master script, it’s attached to a gameobject that’s supposed to be a “processor” for the team:
//what this script does is, it grabs a metric ton of OverlapSphere() commands that are requested by a
//certain amount of gameobjects stored in a list and multi-threads them, then
//the results are thrown into a big array, where all the objects iterate and get their collision results from.
//it is faster than just calling OverlapSphere() on each individual object since OverlapSphereCommand() (it's JOBS counterpart)
//is multithreaded whereas the normal OverlapSphere() command isn't, thus it clogs the main thread and gives a big CPU overhead,
//which isn't really what anyone wants to happen in their game...
//this looks complex but is actually really simple, you just need to write more lines and have two scripts dedicated to this function.
// - sm777
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
using System.Collections.Generic;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
using UnityEngine.Jobs;
//the job:
[BurstCompile]
public struct thejob : IJobParallelFor
{
public NativeArray<OverlapSphereCommand> command;
public NativeArray<Vector3> position;
public int layer;
QueryParameters qp;
public void Execute(int i)
{
qp.layerMask = ~(1 << 0 | 1 << 1 | 1 << 2 | 1 << 3 | 1 << 4 | 1 << 5 | 1 << layer | 1<<15);
command[i] = new OverlapSphereCommand(position[i], 25f, qp); //Max range is 25 meters!
}
}
public class TargetDetectionMaster : MonoBehaviour
{
public List<GameObject> Objects;
public GameObject[] colList;
[SerializeField] int team, maxunits;
public static int MaxHits = 20, maxdist = 25;
///
///
///
///
NativeArray<Vector3> positions;
NativeArray<OverlapSphereCommand> commands;
NativeArray<ColliderHit> hitcolliders;
void Start()
{
team = team + 5;
//the big array in question:
colList = new GameObject[MaxHits * maxunits];
InvokeRepeating("CalculateOverlaps", 0.1f, 0.2f);
}
//what this does is that it processes all those OverlapSphere calls that were made in the IJobParallelFor.
//and it throws them into one big array where the gameobjects can access
void CalculateOverlaps()
{
commands = new NativeArray<OverlapSphereCommand>(Objects.Count, Allocator.TempJob);
hitcolliders = new NativeArray<ColliderHit>(MaxHits * maxunits, Allocator.TempJob);
positions = new NativeArray<Vector3>(Objects.Count, Allocator.TempJob);
thejob ParallelJob = new thejob()
{
command = commands,
position = positions,
layer = team
};
//a "jobless" implementation
// for (int i = 0; i < commands.Length; i++)
// {
// commands[i] = new OverlapSphereCommand(transform.position, 10, QueryParameters.Default);
// }
//grabbing all positions of the objects stored in the list
for (int i = 0; i < positions.Length; i++)
{
positions[i] = Objects[i].transform.position;
}
JobHandle handle = ParallelJob.Schedule(Objects.Count, 1);
handle = OverlapSphereCommand.ScheduleBatch(commands, hitcolliders, 1, MaxHits, handle);
handle.Complete();
//throwing all results into the big array
for (int i = 0; i < colList.Length; i++)
{
if (hitcolliders[i].collider != null)
{
colList[i] = hitcolliders[i].collider.gameObject;
}
else
{
colList[i] = null;
}
}
commands.Dispose();
hitcolliders.Dispose();
positions.Dispose();
}
}
And the target detection manager for the individual units:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TargetDetection : MonoBehaviour
{
public GameObject[] CollidersInRadius;
[SerializeField] int ObjectListIndex;
TargetDetectionMaster OverlapMaster;
int prevcount;
// Start is called before the first frame update
void Start()
{
int team = GetComponent<ITeams>().Team;
OverlapMaster = GameObject.FindGameObjectWithTag("Team " + team + " Processor").GetComponent<TargetDetectionMaster>();
OverlapMaster.Objects.Add(gameObject);
//getting the object's index at the start since it's faster than iterating thru the whole list at runtime
ObjectListIndex = Utilities.GetIndex(OverlapMaster.Objects, gameObject);
CollidersInRadius = new GameObject[TargetDetectionMaster.MaxHits];
StartCoroutine("CheckForIndexAndColliders");
}
void OnDestroy()
{
OverlapMaster.Objects.Remove(gameObject);
}
IEnumerator CheckForIndexAndColliders()
{
while (true)
{
GetMyColliders();
yield return new WaitForSeconds(0.1f);
}
}
void GetMyColliders()
{
if (prevcount != OverlapMaster.Objects.Count)
{
ObjectListIndex = Utilities.GetIndex(OverlapMaster.Objects, gameObject);
prevcount = OverlapMaster.Objects.Count;
}
for (int i = 0; i < CollidersInRadius.Length; i++)
{
CollidersInRadius[i] = OverlapMaster.colList[(TargetDetectionMaster.MaxHits * ObjectListIndex) + i];
}
}
}
if anyone’s confused about “Utilities.GetIndex()”, it’s a custom function. Apparently it’s faster than the out-of-the-box implementation:
public static int GetIndex(IList<GameObject> list, GameObject value)
{
for (int index = 0; index < list.Count; index++)
{
if (list[index] == value)
{
return index;
}
}
return -1;
}
I hope this will be useful for anyone that passes by. If there’s a better way to do this, let me know. This is what i could come up with.
The performance increase is pretty good, i can now have like 300 units without even scratching a dent in the performance. If anything, what actually makes a dent in the performance is Unity’s animator component. Sad that it doesn’t come with some optimizations out of the box, i’ll have to implement those myself. Anyway, i digress.