- Home /
Finding Closest Object with considering the Performance
Hey Guys, Does anyone have an idea how i could find the closest enemy object without ruining the performance by calling it every single frame ?
There are probably thousands of ways (maybe use multiple ray casts? ) but which is the best when it comes to performance ??
Thank you for any help and good night (3:30 am in German time)
You could use an enumerator and yield every x seconds ins$$anonymous$$d for checking. Are you using 2D or 3D?
I'm not going to post my solution because frankly it took me time to think up and write and you won't learn anything by just being given it.
However I will say that the second example here is a good place to start. On paper using FindGameObjectsWithTag frequently sounds like a bad idea but before I wrote my solution I had dozens of objects calling that function every single frame with hundreds of enemies on screen without any noticeable drop in performance.
Point is: write something that works first and don't worry about performance too much until later.
Answer by DesignPie · Mar 27, 2016 at 02:03 PM
Good question. For my projects, I do something by the lines of this;
using UnityEngine;
using System.Collections;
public class ExampleClass : MonoBehaviour {
public Transform target;
public float closeDistance = 25.0F;
void Update() {
if (target) {
Vector3 offset = target.position - transform.position;
float sqrLen = offset.sqrMagnitude;
if (sqrLen < closeDistance)
//If the [target] is closer than [closeDistance] away from me,
//execute code here.
}
}
}
Make sure closeDistance is the squared amount of the distance you want. Usually, the magnitude script performs the sqrt function which is quite memory expensive, so sqrMagnitude is a faster alternative. I'm sure computers can do maths without eating your memory away unlike calculating multiple raycasts on a 3D or 2D space. Hopefully, that helps ;)
This is very similar to the solution I use and there's no problem with performance. $$anonymous$$y game often needs to do 24 characters checking the distance of the 23 other characters every frame and with a similar algorithm, I run at a very s$$anonymous$$dy 60 frames a second.
Unless you're doing hundreds or thousands of checks every frame, the above method works perfectly well.
Answer by TCROC · Mar 27, 2016 at 02:03 PM
Hey I had a similar situation earlier today. I was able to solve it by using Physics.OverlapSphere. This allows you to detect objects around you 360 degrees. If you don't want to call this method every frame, you can call it in a coroutine instead of update. Here is a code example.
using UnityEngine;
using System.Collections;
public class Raycast_ToJump : MonoBehaviour {
float distanceToCheckForEnemy = 1;
// Update is called once per frame
void Update () {
DetectEnemy(transform.position, 1);
}
private void DetectEnemy(Vector3 center, float radius) {
var hitColliders = Physics.OverlapSphere(center, radius);
for (var i = 0; i < hitColliders.Length; i++) {
// collect information on the hits here
print(hitColliders[i].tag);
}
}
}
Answer by Martian-Games · Mar 27, 2016 at 05:57 AM
Here is an optimized solution that allows you to set how often to run a check "findDelay". I'm using the optimized Vector3.SqrMagnitude instead of Vector3.Distance because we only need to know which enemy is closest, not the exact meter distance (which would need Square-Root). The list of enemies is stored at start so you may need to update the list if an enemy is added or removed. Attach on any GameObject in scene, and from any script: EnemyFinder.closestEnemy to access current closest enemy. Hope it helps! :)
using UnityEngine;
using System.Collections;
public class EnemyFinder : MonoBehaviour {
public float findDelay=0.25f;//updates 4 times per second
public static GameObject closestEnemy;
GameObject[] enemies;
void Start () {
enemies=GameObject.FindGameObjectsWithTag("Enemy");
InvokeRepeating("findClosestEnemy",0f,findDelay);
}
public void findClosestEnemy(){
float smallestDist=float.MaxValue;
Vector3 playerPos=Camera.main.transform.position; // assuming player is in same position as main camera
GameObject newClosestEnemy=closestEnemy;
foreach(GameObject enemyObj in enemies){
float dist=Vector3.SqrMagnitude(enemyObj.transform.position-playerPos);
if(dist<smallestDist){
newClosestEnemy=enemyObj;
smallestDist=dist;
}
}
if(newClosestEnemy!=closestEnemy){
closestEnemy=newClosestEnemy;
print("NEW Closest Enemy = "+closestEnemy);
}
}
}
Invoke repeating is okay, but it wont give you much control over delay except for when it very first executes the repeating function and from what I've been told string functions execute slower.
I've found InvokeRepeating has a slight performance benefit over IEnumerator when it does not need to be called more than once. But indeed IEnumerator would be favorable if control is needed over the update frequency.
Your answer