- Home /
Most efficient way to check distance between player and scene objects
Hi. I need various objects in each of my scenes to do something when the player is within a certain distance. Is it better to attach the distance checking script to each object or to attach it to the player and loop through each object?
Im using c#
Thanks!
Thanks for the information, I will try to figure it out for more. Keep sharing such informative post.
Answer by vexe · Sep 14, 2013 at 05:43 AM
Interesting question. In my opinion do what @iwaldrop told you, logic-wise. Really nice logic he made there (+1) it depends on who's doing what.
However performance-wise, It's not like you're gonna get a boost if you're gonna calculate the distance from your objects (or vise versa), simply because for example if you have 100 objects, the distance instruction will get executed 100 times no matter what (let it be Vector3.Distance
)
To prove it, I went in for some benchmarking. Here's my test environment:
I have a CubeSpawner.cs
script attached to my main camera, that spawns a number of cubes:
public class CubeSpawner : MonoBehaviour
{
public int nCubes;
public GameObject cube;
public List<GameObject> allCubes = new List<GameObject>();
private Transform _transform;
void Awake()
{
_transform = transform;
}
IEnumerator Start()
{
for (int i = 0; i < nCubes; i++)
{
var randVect = new Vector3(Random.Range(-50, 50),
Random.Range(-50, 50),
Random.Range(-50, 50));
allCubes.Add((GameObject)Instantiate(cube, _transform.position + randVect, Quaternion.identity));
yield return null;
}
var watch = new Stopwatch();
watch.Start();
for (int i = 0, len = allCubes.Count; i < len; i++)
{
var dist = Vector3.Distance(_transform.position, allCubes[i].transform.position);
yield return null;
}
watch.Stop();
Debug.Log("SPAWNER to CUBE: " + watch.ElapsedMilliseconds);
Debug.Log("CUBES to SPAWNER: " + CubeScript.totalTime);
}
}
Here's my CubeScript.cs
:
public class CubeScript : MonoBehaviour
{
public Transform target;
public static double totalTime = 0;
private Transform _transform;
void Awake()
{
_transform = transform;
target = Camera.mainCamera.transform;
}
IEnumerator Start()
{
var watch = new Stopwatch();
watch.Start();
// var init = Time.realtimeSinceStartup;
var dist = Vector3.Distance(_transform.position, target.position);
yield return null;
// totalTime += (Time.realtimeSinceStartup - init);
watch.Stop();
totalTime += watch.ElapsedMilliseconds;
}
}
(The comments shows another way to get the time took to get the dist) totalTime
is static so its shared between all cubes.
Now, you might notice a strange thing, why am I doing a yield return null
? Because no matter how I tried getting the total time without it, I just kept getting a zero, because it takes very little time to do a single Vector3.Distance
- You can try it out for yourself.
Now that there's a yield return null
after each distance calc in both the spawner and the cube scripts, we should reach balance now.
I ran a few tests, here are the results of instantiating 1000 cubes (you can think of the spawner as your player, and the cubes as the objects):
Test#: 1 2 3 4
SPAWNER to CUBES: 3683, 3208, 3108, 3514 (ms)
CUBES to SPAWNER: 3776, 3240, 3461, 3453 (ms)
So, as you can see they're very close, with more wins to SPAWNER to CUBES
over CUBES to SPAWNER
So performance-wise, it doesn't make that much of a difference.
And if there was a very huge number of objects that you must calculate the dist to (like 100000), I would use a yield return null
to divide the pressure over separate frames every 1000 call or so.
Note that I used Vector3.Distance
just to illustrate, it's more efficient to use (pos1 - pos2).sqrMagnitude
if you just wanna compare between two distances like:
(pos1 - pos2).sqrMagnitude < dist * dist
Answer by iwaldrop · Sep 14, 2013 at 05:03 AM
You can do it either way, but I'd reccomend thinking about what you want to do. If the player is going to do something when these object get in range, have the player do it.
foreach (Transform t in otherTransforms)
if ((transform.position - t.position).sqrMagnitude < distance * distance)
// do something
If the objects are going to do something, have them check.
if ((StaticPlayerPositionPropery - transform.position).sqrMagnitude < distance * distance)
// do something
Otherwise, if both things are going to do something, it's better to have an observer do it.
foreach (Transform t in objectTransforms)
if ((OnObjectInRange != null && StaticPlayerPositionProperty - t.position).sqrMagnitude < distance * distance)
OnObjectInRange(t);
In your for next loop (the first one) you meant t.transform.position right?
It would also be better to cache transform.position for that loop for two reasons - doing transform takes some time, and calculating a position can be expensive especially if items have parents.
Yes whydididoit, thanks for the catch; fixed. And I totally agree with the position caching, just wanted to provide a concise example.
Wow thanks all!
That answers my question :) and I think the logic fits more in line with my game play. I will put the script on the player.
Answer by Ashutosh8126 · Sep 14, 2013 at 06:46 AM
I won't recommend checking distance.. I prefer saving calculations.. I would prefer using a trigger maintains its size at the necessary distance you want.. and as any scene object collides it, then you can do anything..
Thats also an interesting idea. Behind the scenes wouldnt this mean alot of math checking of the player is within the bounds of each and every trigger object?
No.. I believe this is the most optimized way.. ins$$anonymous$$d of checking the distance between the player and the objects in Update :)
Answer by ginryu · Oct 24, 2021 at 10:58 PM
if you want to just check to see if an if an object is within the bounds of an object, instead of using distance, just take the height,width, length of an object and check to see if the players position falls within those coordinates
Your answer
Follow this Question
Related Questions
The name 'Joystick' does not denote a valid type ('not found') 2 Answers
Distribute terrain in zones 3 Answers
Multiple Cars not working 1 Answer