This is in unity 2019.4.40f1. I'm trying to rotate an object toward it's movement direction. However, I noticed that this is inaccurate and in investigating I noticed that the inaccuracy comes from the actual movement amounts. What I mean is, in the code below, delta.y / delta.x
prints out different values every loop iteration even though destination
does not change. As a result, Atan2
of course also returns different results. The error every frame is on the 4th 3rd or 4th decimal place, however, over just a few seconds it adds up because it's not random variation but instead seems to be systematic.
Also note that at this stage I'm not actually applying the rotation to the object. My expectation is that the code below should move the object from its position to the destination a little bit every frame, in a straight line. This does appear to be the case visually, but numerically there is enough deviation to cause problems with the angle calculations.
IEnumerator TweenFunc (object myObject, Vector2 destination, float speed)
{
while (condition)
{
currentPosition = myObject.transform.position;
delta = destination - currentPosition;
Vector2 dir = delta.normalized;
frameTime = TimeManager.FrameTime
myObject.transform.Translate(dir.x * frameTime * speed, dir.y * frameTime * speed, 0);
Debug.Log(delta.y / delta.x);
Debug.Log(Mathf.Rad2Deg* Mathf.Atan2(delta.y, delta.x));
yield return true;
}
}
I would like to keep the angle computation consistent. It is possible to pre-compute the angle once, outside of the loop by computing delta first outside of the loop and then getting the angle before entering the loop. However, this solution is not usable in my use-case because the actual angle computation needs to take place in a different part of the code base that is only aware of dir
and speed
and not destination
.
Edit for additional info: When traveling from (1.2, -0.2)
to (0.2, -3.2)
the angle changes from ~108.43352
to 108.3664
. The true angle should be 108.435
. While this is only a 10th of a degree, if I actually apply the rotation to the object it becomes visible, especially because most of the change appears to happen toward the end of the movement. I wonder if as dir.x and dir.y go to zero accuracy becomes worse in a systematic way. Also, there is no angle drift when traveling in any of the cardinal directions. There, the accuracy of dir.x and dir.y don't require decimal places since they are either 0 or 1, so that could be what's going on.
I wonder if there's a way to deal with this since the ultimate goal is actually a pretty common thing - to rotate the object toward its movement direction.
CodePudding user response:
Even though the code you show here only does translation an not rotation, in general have in mind that Transform.Translate
by default works in local space.
So if (from your title I just assume) you object is rotated this is actually inaccurate since you take a world space delta
and then move on it but in local space
=> You want to explicitly pass in
myObject.transform.Translate(dir.x * frameTime * speed, dir.y * frameTime * speed, 0, Space.World);
And then you probably want to rather operate on the actually normalized movement vector and afaik you flipped the order of arguments:
Debug.Log(Mathf.Rad2Deg * Mathf.Atan2(dir.x, dir.y));
CodePudding user response:
I'll add my partial solution answers since I haven't figured out how to counteract the issue directly.
It's possible to cast the x and y movement direction and all subsequent calculations leading to the angle computation as doubles by computing
destination - currentPosition
normally, casting that to double, and doing the rest of the vector computations (e.g. normalization, Atan2) manually with double precision and usingMath
rather thanMathf
functions. Since object transform positions are still stored as floats there is still error, but this results in a reduction in error in the example of0.05
of a degree, so it reduces the error in that example by half.Since the function
TweenFunc
does not update destination, and it's not expected that game objects should be moved outside of the function while it is active, I can move the computation ofdir.x
anddir.y
outside of the loop, so they are only computed once, at the beginning, at maximum accuracy, which in the example returns-108.4349
and no additional error accumulates further.
This works for producing stable movement directions, but the major downside of this is if somehow the game object is moved in some way outside of this function, then it may never reach its destination and we might therefore never exit the loop inTweenFunc
. This can be addressed by adding complexity like checking if movement direction changed by more than 1 degree or some other threshold, and resetting it then.