Home > Mobile >  Rotating an object to the movement direction is systematically inaccurate
Rotating an object to the movement direction is systematically inaccurate

Time:11-17

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.

  1. 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 using Math rather than Mathf 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 of 0.05 of a degree, so it reduces the error in that example by half.

  2. 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 of dir.x and dir.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 in TweenFunc. 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.

  • Related