- Home /
Accuracy issues with Float (Vector3)
Hi.
I wrote an NPC movement script, which includes the rotation of the NPC-character in order to make it look forwards after changing its direction. It works like follows: After the movement to a certain point, it the NPC turns around until the correct rotation is reached, and then starts moving.
The problem is that it sometimes get stuck at rotating. Obviously this is caused by the floating point inaccuracy. I checked that by printing the result of the == opertion where I compare the normalized vectors, and it returns false. I also printed the values of the vectors to see how big the inaccuracy is:
(0.9831, 0.0000, 0.1830) (0.9824, 0.0000, 0.1866)
I know that the == operator has approximation integrated, so I am quite astonished that this simple rotation already causes problems.
Are there any ways to prevent this (globally or in that special case). I honestly don't even know if the script does a good job, I started with unity a few days ago. Any advice would be appreciated.
Edit: Here the complete code. Nothing is accessed by other scripts.
[RequireComponent(typeof(Animator))]
public class Patrol : MonoBehaviour {
public Transform[] patrolPoints;
private Transform current;
private int currentPoint = 0;
public float moveSpeed;
public float animSpeed = 1.5f;
private Animator anim;
[Range(0,360)]
public float rotationSpeed;
void Start () {
transform.position = patrolPoints[currentPoint].position;
current = patrolPoints[currentPoint];
anim = gameObject.GetComponent<Animator>();
anim.speed = animSpeed;
anim.SetFloat("Speed", moveSpeed);
}
private bool rotating = false;
void Update() {
if (equals(transform.position,current.position))
{
currentPoint = (currentPoint + 1) % patrolPoints.Length;
current = patrolPoints[currentPoint];
rotating = true;
}
if (!rotating)
{
transform.position = Vector3.MoveTowards(transform.position, current.position, moveSpeed * Time.deltaTime);
}
else {
Vector3 targetDir = current.position- transform.position ;
transform.rotation = Quaternion.LookRotation(Vector3.RotateTowards(
transform.forward,
targetDir, rotationSpeed*Time.deltaTime, 0));
if (equals(targetDir.normalized, transform.forward.normalized))
{
rotating = false;
}
}
}
you're saying that modifying the transform.forward
value is also modifying the transform.position
? that does seem weird. can you make a $$anonymous$$imal repro of that ?
for what it's worth, i often use a utility function of my own: bool isWithinEpsilon(float a, float b, float epsilon = 0.0001f) { float diff = b - a; return ((-epsilon < b) && (b < epsilon)); }
Answer by Zeoli · Mar 28, 2016 at 06:19 AM
I believe you may need to mess around with this Mathf function. http://docs.unity3d.com/ScriptReference/Mathf.Approximately.html
I didn't know about the Approximately-function, thanks. But that is not enough. Comparing (0.9832, 0.0000, 0.1827), (0.9824, 0.0000, 0.1866) returns false for the z-values, which are 0.0039 apart. I guess I have to compare it manually then?
You can also try using the $$anonymous$$ath.Round method (using System). Note that it's not $$anonymous$$ath*f*.Round. $$anonymous$$ath.Round accepts an int parameter to pass the desired number of decimal places that should be rounded to.
Answer by phxvyper · Mar 31, 2016 at 07:20 PM
You've been misinformed. The == operator is an equality operator and does not have any checks for approximation. This is why Mathf.Approximately exists.
Personally, I believe there's something in your code for rotation or movement that you can change in order to fix this issue. However, what you can do is create your own form of Approximately, like I do on occasion:
public bool Approximately(float a, float b, float epsilon)
{
return (Mathf.Abs(a - b) < epsilon) || (Mathf.Approximately(Mathf.Abs(a - b), epsilon));
}
//or JS:
function Approximately(a : float, b : float, epsilon : float) : float
{
return (Mathf.Abs(a - b) < epsilon) || (Mathf.Approximately(Mathf.Abs(a - b), epsilon));
}
epsilon is the maximum distance between two floats that you'll accept as approximately.
Approximately(0.05f, 0.1f, 0.05f) //will return true
Approximately(0.06f, 0.1f, 0.04f) //will return true
Approximately(0.05f, 0.1f, 0.06f) //will return false
Actually the Vector3 equality operator does some approximation. It's implemented like this:
public static bool operator ==(Vector3 lhs, Vector3 rhs)
{
return Vector3.Sqr$$anonymous$$agnitude(lhs - rhs) < 9.99999944E-11f;
}
The question was about comparing Vector3 values, not float values. However the error is way beyond most approximation limits. When dealing with vectors you never really compare two vectors directly. You usually check the distance between them against an epsilon value.
When using Sqr$$anonymous$$agnitude keep in $$anonymous$$d to square the epsilon as well. So if the distance between the vectors should be lower than 0.01f you have to compare against (0.01f² == 0.0001f). Sqr$$anonymous$$agnitude is a bit faster to calculate than magnitude as it doesn't require a square root.
Oh i thought he was talking about directly comparing floats, not Vectors. Whoops!
Personally, I believe there's something in your code for rotation or movement that you can change in order to fix this issue.
I edited the post and provided the full code, perhaps you can find something.
Have you tried Slerp'ing the rotation ins$$anonymous$$d of using RotateTowards?
Your answer
Follow this Question
Related Questions
How can I replace this Vector3 with a float? 2 Answers
Calculating Scrolling GameObject x position scrolling pass another GameObject x postion (2D Game) 1 Answer
Velocity value changes way to drastically when position quickly changes 0 Answers
Android Gyroscope inaccuracy problems. 1 Answer
problem with object rotation 1 Answer