Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Different Ways to Find Distance

Discussion in 'Scripting' started by netside, Dec 12, 2014.

  1. netside

    netside

    Joined:
    Nov 3, 2014
    Posts:
    9
    I've been learning Unity scripting lately, and I've been looking for ways to find the distance, and came upon two different ones.

    The Unity manual/documentation (here: http://docs.unity3d.com/Manual/DirectionDistanceFromOneObjectToAnother.html) has a few different statements you can use to calculate the distance, but the "Unity Scripting API" (here: http://docs.unity3d.com/ScriptReference/Vector3.Distance.html) uses a function build into Unity (Vector3.Distance) to find the distance.

    Which way should people use, and which manual should I be reading from, out of these two?

    Just wondering. Thanks...
     
  2. toreau

    toreau

    Joined:
    Feb 8, 2014
    Posts:
    204
    As always, use the one that suits you best.
     
  3. Todd-Wasson

    Todd-Wasson

    Joined:
    Aug 7, 2014
    Posts:
    1,077
    This example:

    http://docs.unity3d.com/Manual/DirectionDistanceFromOneObjectToAnother.html

    is using the Vector3 class when it does "heading.magnitude." "Heading" is a Vector3. So in the manual it's teaching generally how to do something while the scripting API is showing specifically what all the methods and so on do. I refer to both. Like toreau said, just use whatever suits you best.
     
  4. Todd-Wasson

    Todd-Wasson

    Joined:
    Aug 7, 2014
    Posts:
    1,077
    If what you're looking to do is find distance, I avoid using the Vector3 class methods because they're relatively slow, probably due to the function call that's done when internally the math is very simple. For instance this:

    Code (csharp):
    1.  
    2. // Gets a vector that points from the player's position to the target's.
    3. Vector3 heading = target.position - player.position;
    4.  
    is many times slower than doing this:
    Code (csharp):
    1.  
    2. Vector3 heading;
    3. heading.x = target.position.x - player.position.x;
    4. heading.y = target.position.y - player.position.y;
    5. heading.z = target.position.z - player.position.z;
    6.  
    There are no method calls here at all. I haven't tested "magnitude" but suspect it's similar. Square roots are very fast these days, not the big deal they used to be.

    What I do now is prototype code using the Vector3 classes for dot, cross, distance, distance squared, and so forth, then when the algorithm is working I go back in and hand code all of it to eliminate the Vector3 method calls. I got my boat simulation physics to run about 10 times faster by doing that, some parts of it were up to 80 times faster. It really makes a difference.

    Whether or not it's worth the trouble depends what you're doing though. If you're only doing a distance calculation every now and then, it's not worth bothering with, but if you're doing thousands per second it starts to add up.
     
  5. Todd-Wasson

    Todd-Wasson

    Joined:
    Aug 7, 2014
    Posts:
    1,077
    You got me curious so I decided to profile it in Unity 5. Here's the test script:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class Test : MonoBehaviour {
    6.  
    7.     private Vector3 firstPosition;
    8.     private Vector3 secondPosition;
    9.  
    10.     // Use this for initialization
    11.     void Start()
    12.     {
    13.         firstPosition = new Vector3(1, 1, 1);
    14.         secondPosition = new Vector3(2, 2, 2);
    15.  
    16.     }
    17.  
    18.     void Test1()
    19.     {
    20.         for (int i = 0; i < 10000; i++)
    21.         {
    22.             Vector3 heading = firstPosition - secondPosition;
    23.             float distance = heading.magnitude;
    24.         }
    25.     }
    26.  
    27.     void Test2()
    28.     {
    29.         for (int i = 0; i < 10000; i++)
    30.         {
    31.             float distance = Vector3.Distance(firstPosition, transform.position);
    32.         }
    33.     }
    34.  
    35.     void Test3()
    36.     {
    37.  
    38.         Vector3 heading;
    39.         float distance;
    40.         Vector3 direction;
    41.         float distanceSquared;
    42.  
    43.         for (int i = 0; i < 10000; i++)
    44.         {
    45.             heading.x = firstPosition.x - secondPosition.x;
    46.             heading.y = firstPosition.y - secondPosition.y;
    47.             heading.z = firstPosition.z - secondPosition.z;
    48.  
    49.             distanceSquared = heading.x * heading.x + heading.y * heading.y + heading.z * heading.z;
    50.             distance = Mathf.Sqrt(distanceSquared);
    51.  
    52.             direction.x = heading.x / distance;
    53.             direction.y = heading.y / distance;
    54.             direction.z = heading.z / distance;
    55.         }
    56.     }
    57.  
    58.     // Update is called once per frame
    59.     void Update()
    60.     {
    61.         Test1();
    62.         Test2();
    63.         Test3();
    64.     }
    65. }
    66.  
    67.  
    Test1() uses the approach here: http://docs.unity3d.com/Manual/DirectionDistanceFromOneObjectToAnother.html

    I stop short of computing the direction and just do distance there.

    Test2() uses the Vector3.Distance() method from here: http://docs.unity3d.com/ScriptReference/Vector3.Distance.html

    Test3() does what I was suggesting by eliminating all of the Vector3 method calls. Here are the times:

    Test1() 2.91 ms
    Test2() 4.68 ms (or 7.11ms with 2 transforms)
    Test3() 0.93 ms

    With this test, Vector3.Distance as used in Test2() was slowest, probably because it's accessing a transform in the middle of it along with the method calls. If you do two transforms in there which is probably more like your typical case (instead of one transform and one vector), it slows down more to 7.11 ms. So while it's easy to use and you can do the distance computation in one line of code, it's relatively slow because of at least two internal method calls including a pair of Transform.get_position() calls. I'd use it for prototyping, but probably not in actual code unless it was not being used very often.

    Test1() is faster in this test just because I was using two vectors. In the more typical case you'd probably use transform.positions in there. I tested that and it slows it to 7.79ms which is even slower than Test2() with the Vector3.Distance method call.

    Contrast the 7 to 8 ms call with Test3() which is the hand coded version with a single Mathf.Sqrt() call without any transforms in it and you're almost 10 times faster. If you plop in transform.position in place of firstPosition and secondPosition, it ends up being twice as slow as the other two because you're doing Transform() over and over again. In that case you're better off just using the Vector3.Distance() function and being done with it.

    Performance comes down largely to caching the variables and being clever with how you do the computations in the end. If you're doing large numbers of distance computations on lots of objects, it may be best to stuff the data into a couple of big arrays and loop over them all in one shot in a single method call. These kinds of tricks got me at least 10 times faster physics code in the end. The profiler in Unity Pro is very good, there's a lot to be gained with it.
     
  6. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,523
    Just adding that, most of the time, you can skip the sqrt entirely. Sqrt is an expensive operation. If you want to compare two distances to see which is closer, it's the same to compare two distanceSquared values. If you want your NavMeshAgent to stop when it's 2 units from its destination, stop when distanceSquared is 4.
     
    kustom likes this.
  7. BBeck

    BBeck

    Joined:
    Jan 12, 2014
    Posts:
    57
    You have to realize that vectors are based on trigonometry. And the distance equations are pretty much based on trigonometry. It's kind of 6 of one and half a dozen of the other. I mean, you could trace all the way back through the math and see which requires the largest number of math operations, but to do it right you need to find out how many CPU cycles each operation takes to see which is truly the most expensive. Doing a head to head comparison, like Todd Wasson did, is probably the best way to find out which is faster. But under the hood you're doing basically the same thing either way.

    Here is the raw distance formula. That's the method that's going to make the most sense to most people who don't understand vectors. But the vector math is doing basically the same thing. You're subtracting one point from the other like this video demonstrates. But then you still have to find the magnitude of the result vector in order to know the distance between the two. As this shows, calculating the magnitude of a vector still uses the Pythagorean theorem (in this case the 3D Pythagorean theorem).

    So basically, its two different ways of doing the exact same thing. The magnitude equation for vectors uses the distance formula to find the magnitude of a vector. Notice that in the distance formula example, the first thing you did was subtract the two points and get them related to the origin at 0,0,0. That's basically the same thing as subtracting two vectors. Then you used the 2D or 3D Pythagorean theorem, as appropriate, to calculate the actual distance between the two points. That's exactly what the magnitude equation does.

    Six of one. Half a dozen of the other. You can probably find other ways to calculate distance too. But it's basically all about dealing with the length of a line that is not aligned with the axis which will involve some sort of angular calculation and subtraction.
     
  8. Todd-Wasson

    Todd-Wasson

    Joined:
    Aug 7, 2014
    Posts:
    1,077
    Good posts, guys.

    I guess it comes down to execution speed versus ease of use. For speed what I try to do is pay close attention to what's being accessed. For instance, if you use transform.position somewhere or access another property, it's going to slow things down just because it's doing a function call somewhere rather than just reading a variable straight out of memory. That's extra CPU cycles and memory reads. Sometimes there's no way around it though, you have to get the positions if you want to compute distances.

    The easiest way to do it is probably to just use Vector3.Distance. The ease comes at a price, but if you're only doing it a few times per frame it's not worth worrying about. My boat sim does thousands and thousands of forces, cross and dot products in the computation of those forces, etc., so speed is really critical. I even go to the trouble of computing the torque vectors myself to replace a lot of the work rigidbody.AddForceAtPosition (or whatever it's called) has to do. When computing the forces, I add them into a big array of forces and positions, then cycle through them all in one function call, accumulating the total force and total torque in a float variable in a loop. In there the torque is computed from the position, etc..

    At the end of the loop I do a single AddForce and a single AddTorque call. Presto, thousands of rigidBody.AddForceAtPosition calls have been eliminated and replaced with just two calls. It makes the code a lot bigger, but it's much faster. Granted, if someone isn't sharp on their 3D math it's probably not something to chase, and if you're not doing a lot of dots/crosses/forces then it's not worth the trouble anyway.
     
  9. Todd-Wasson

    Todd-Wasson

    Joined:
    Aug 7, 2014
    Posts:
    1,077
    Square root is really fast nowadays, I wouldn't worry so much about it. It's cheaper not to do it of course, but having said that, it's not much more expensive than a couple of additions probably on today's CPU's. Square root is even faster than taking the absolute value of a number, believe it or not. In parts of my code where I do lots of absolute values, I actually replace those with square roots and a few multiplications because all that combined is still faster than Mathf.Abs().. Seems crazy, but that's what the profile showed.

    For comparing distances of course it's better to just use the squared distance. No matter how fast square root is, NOT computing it is still going to be faster! :D
     
    Last edited: Dec 12, 2014
  10. toreau

    toreau

    Joined:
    Feb 8, 2014
    Posts:
    204
    Benchmarks are generally both interesting and intriguing, but wouldn't it be _really_ interesting if these three methods created the same result? :)

    I stripped down your code to just care about calculating the distance between the two Vector3s, and the results doesn't match;

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Test : MonoBehaviour
    4. {
    5.     Vector3 firstPosition = new Vector3( 1, 1, 1 );
    6.     Vector3 secondPosition = new Vector3( 2, 2, 2 );
    7.  
    8.     void Start ()
    9.     {
    10.         Debug.Log ( "Test nr. 1 = " + Test1 () );
    11.         Debug.Log ( "Test nr. 2 = " + Test2 () );
    12.         Debug.Log ( "Test nr. 3 = " + Test3 () );
    13.     }
    14.  
    15.     float Test1 ()
    16.     {
    17.         Vector3 heading = firstPosition - secondPosition;
    18.         return heading.magnitude;
    19.     }
    20.  
    21.     float Test2 ()
    22.     {
    23.         return Vector3.Distance( firstPosition, transform.position );
    24.     }
    25.  
    26.     float Test3 ()
    27.     {
    28.    
    29.         Vector3 heading;
    30.         float distanceSquared;
    31.         float distance;
    32.  
    33.         heading.x = firstPosition.x - secondPosition.x;
    34.         heading.y = firstPosition.y - secondPosition.y;
    35.         heading.z = firstPosition.z - secondPosition.z;
    36.    
    37.         distanceSquared = heading.x * heading.x + heading.y * heading.y + heading.z * heading.z;
    38.         distance = Mathf.Sqrt(distanceSquared);
    39.    
    40.         return distance;
    41.     }
    42. }
    This results in the following values:

    Code (CSharp):
    1. Test nr. 1 = 1.732051
    2. Test nr. 2 = 11.04536
    3. Test nr. 3 = 1.732051
    Am I missing/misunderstanding something?

    EDIT: My bad, of course. I forgot to replace transform.position with secondPosition on line #23. I left my error for future reference.
     
    Last edited: Dec 15, 2014
  11. netside

    netside

    Joined:
    Nov 3, 2014
    Posts:
    9
    Wow, I didn't realize I'd be starting such a controversial thread.

    Thanks for all the answers and posts.. I have a much better grip on the vector-math stuff now because of this.
     
  12. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,398
    Yep, "square root is expensive" hasn't been true for years. In fact a decent chunk of the time it takes to do System.Math.Sqrt (or Mathf.Sqrt) is actually the function call overhead. Along those lines, division is also not slow anymore, so going through contortions to avoid it is quite inadvisable. On my CPU, + - / * all take the same number of clock cycles.

    --Eric
     
    MNNoxMortem likes this.
  13. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,523
    I guess I should've just said, if you don't need to do something, don't do it. It's faster to do nothing than to do something, no matter how fast it is. That was my only point. :)
     
  14. TheBestTroll

    TheBestTroll

    Joined:
    Nov 22, 2019
    Posts:
    6
    Wow. I think I am little late haha. But I wanna ask a small question about this.
    I have a script on my game. It's working nicely, but I always think I can make it run smootherly.
    When I click an object in the game, I want to make a Lerp of its position, and the mousePosition related to world space.
    But I dont want that happens forever, so I want it to happen only when the distance between the object's position and its goal position is greater than 10. (I know, seems weird, but it's exactly what I want)
    Then, my question: how can I do that with the smallest processing price? Rather check in a if using Vector3.Distance(), or the way @Todd-Wasson described in Test(3)?

    Here's what I already have:

    Code (CSharp):
    1. void Update ()
    2.     {
    3.  
    4.         if (beingClicked) {
    5.  
    6.             goalPos = cam.ScreenToWorldPoint (Input.mousePosition);
    7.  
    8.         }
    9.  
    10.         //
    11.  
    12.         if (Vector3.Distance (position, goalPos) > 10f) {
    13.  
    14.             moving = true;
    15.  
    16.             position = Vector3.LerpUnclamped (position, goalPos, Time.deltaTime * 10);
    17.             position.z = 0;
    18.  
    19.             transform.position = position;
    20.  
    21.         } else {
    22.  
    23.             moving = false;
    24.  
    25.         }
    26.  
    27.         //
    28.  
    29.     }
    I almost forgot to say: it's a 2D game, but I'm using Vector3 cause I think it's better to do that than to convert to Vector2, but I dont know, I may be wrong. Please tell me if I am.

    Any help is welcome! Thanks in advance!

    Lucas
     
    Last edited: Apr 4, 2021