Vector2.Dot not returning 0
I have this bit of code here which uses Vector2.Dot to find out whether or not a hunter is behind it's prey.
Vector2 preyWorldDir = prey.transform.TransformDirection (Vector3.left);
Vector2 huntToPreyDir = transform.position - prey.transform.position;
/*Dot Product*/
if (Vector2.Dot (preyWorldDir, huntToPreyDir) < 0)
print ("< 0");//Hunter is behind prey
else if (Vector2.Dot (preyWorldDir, huntToPreyDir) > 0)
print ("> 0");//Hunter is in front of prey
else
print ("= 0");//Hunter is perpendicular of prey
It does it job with finding out whether the hunter is behind/in front of the prey. But not when it's perpendicular. Vector2.Dot never returns 0 when the prey is facing 90 degrees away from the hunter. I can work around this, but it's really just bugging me and I have a feeling this will cause issues in the future.
Answer by Eno-Khaon · Jul 10, 2016 at 05:20 PM
Welcome to the wonderful world of floating point inaccuracy!
Zero happens to be an extremely unlikely number to see in a floating point variable. This is because the closer the number gets to 0, the closer it can be to 0.
What does that mean?
Floating point numbers are stored in a form similar to scientific notation. This combines (something like) an integer with a power-of-10 exponent. To give an example of this:
Let's use 7823846 as our base value. Then, let's turn that into a very large and a very small number, such as 7.823846e10 and 7.823846e-7, or 78238460000 and 0.0000007823846 respectively.
If you increased the base integer value by 1 at this point, you would see a change of 1000 or 0.0000000000001 respectively. There is a limit on how long the base value can be, as well as how far the decimal point can be placed along the number in either direction.
In short, what this means in this case is that while the number can reach 0, it is extremely unlikely when paired with slight inaccuracy of floating point numbers. For instance, if you've ever seen something like 0.999999999 instead of 1, it's basically indistinguishable in truth, but the number appears to be drastically different.
Well, this is the same sort of dilemma. You have near-infinite granularity available as you get closer to 0 and it becomes less and less likely that the floating point value will accurately calculate a result of 0.
This is why Unity has a simple, convenient workaround to accommodate this accuracy issue for minute differences between numbers.
float differenceDot = Vector2.Dot (preyWorldDir, huntToPreyDir);
if(Mathf.Approximately(differenceDot, 0.0f))
print("= 0");
else if(differenceDot > 0)
print("> 0");
else// if(differenceDot < 0)
print("< 0");
That was extremely helpful, but it's still not returning perpendicular. differenceDot ends up being 1.192093E-07, which the hunter then takes as "$$anonymous$$ove closer to prey" then drops down to -0.00999988 after the first update of moving, then differenceDot gradually gets smaller each update the hunter moves closer. Which I do not understand as it shouldn't since the vectors never change.
float d = Vector2.Dot (preyWorldDir, huntToPreyDir);
print (d);
if ($$anonymous$$athf.Approximately (d, 0.0f))
print ("Perpendicular");
else if (d > 0)
Strafe (Vector2.up, prey.transform.position);//Hunter strafes around prey to get behind it
else
$$anonymous$$ove (prey.transform.position, 0.16f);//0.16f is just how close the hunter will get to the prey before it stop moving
Well, it makes perfect sense, actually, that the values would constantly change in general with any movement, but there's another reason entirely for that.
Vector length is a factor in the outcome of a dot product. For example, if you normalized both vectors, the output would range from -1 to 1. If both vectors had a length of 2, however, the output would range from -4 to 4 because their components are being multiplied together. Likewise, the values can also wind up being very small very easily.
Just found out what's causing the such small amount of change. Even though the prey and hunter are on the same exact Y axis (0) the hunter for some reason rotates it self on the Z axis by 0.5729387. Still have no idea why it does that.
Edit: It puts it self on the Y axis of 0.009800009 for some reason when the game starts. Still leaves a mystery.
I still have a problem I need to solve, but I feel like this answered the Vector2.Dot problem so there's no more reason to say my question wasn't answered.
Actually the floating point precision has nothing to do with this problem. It's actually the other way round. The more precise the number representation is, the unlikely it is to get 0 as result. You can only get 0 if the vectors are indeed exactly 90° or due to rounding errors which is more likely the case. Using $$anonymous$$athf.Approximately actually decreases the accuracy by defining a small range which is considered "0".
There is almost no usecase where you want to know if two vectors are exactly perpendicular. So the problem is more a conceptual issue. You usually want to know if they are in a certain area / range. In most cases you want to split the full circle into 4 equal quadrants like i did in my second example.
Answer by Bunny83 · Jul 10, 2016 at 05:04 PM
It's almost impossible that Dot returns 0 since you work with floating point values. It only returns 0 when it's exactly 90°. If there is a small difference (0.000001) it won't return 0.
If you want to know when it's "about" 90° you might want to do this:
float d = Vector2.Dot (preyWorldDir, huntToPreyDir);
if (d < -0.9f)
print ("behind");//Hunter is behind prey
else if (d > 0.9)
print ("in front");//Hunter is in front of prey
else
print ("perpendicular");//Hunter is perpendicular of prey
If you want to split the area at the 45° angle into 4 quadrants you might want to use 0.7071 (cos(45°)). That way the "front" area has a size of 90° (+-45°) as well as the "back" area. Of course both sides also have that size.
if (d < -0.7071f)
print ("behind");//Hunter is behind prey
else if (d > 0.7071)
print ("in front");//Hunter is in front of prey
else
print ("perpendicular");//Hunter is perpendicular of prey
edit
I have missed that little detail about your code. As said in the comment below you haven't normalized your "huntToPreyDir" vector. If you don't work with normalized vectors the result of the dot product has a different meaning.
The dot product simply does this:
float Dot(Vector2 a, Vector2 b)
{
return a.x * b.x + a.y * b.y;
}
The nice property about the dot product is this relation:
Dot(a, b) = |a| * |b| * cos(angle);
or written in Unity C#_
a.magnitude * b.magnitude * Mathf.Cos(angle);
where angle is the angle between the two vectors.
If both vectors are normalized that means the length / magnitude of each vector is "1f" so the dot product simply returns the cos(angle) between the two vectors. This is what you want most the time.
If only one vector is normalized and the other isn't you actually project the length of the unnormalized vector onto the other vector.
If both vectors are not normalized the result of the dot product would be the area of the parallelogram with the two side length "a" and "b".
So you should normalize your vector like this:
Vector2 huntToPreyDir = (transform.position - prey.transform.position).normalized;
Just tried it, it returns perpendicular when it is. But also when it's not? Hard to explain, but I have the hunter strafing around the prey currently, telling when it's perpendicular to the prey or not and it's returning perpendicular when it's not.
Well you haven't normalized your "huntToPreyDir". So the result depends on the distance of the two objects:
Vector2 huntToPreyDir = transform.position - prey.transform.position;
huntToPreyDir.Normalize();
or
Vector2 huntToPreyDir = (transform.position - prey.transform.position).normalized;