# Rotation precision problem

Any way to get around this problem?

``````transform.Rotate( new Vector3(1,1,0));
transform.Rotate( new Vector3(-1,-1,0));
``````

The object will still rotate even when the previous value is inverted. I am guessing it has something to do with percision lost when going from Vector3 to Quaternion.

If I rotate around a single axis (e.g. Vector3(1,0,0) and then invert the rotation using Vector3(-1,0,0)) the object is stationary as it should be - but not for multiple axis.

I’ll also explain why I am trying to do this.

I want to create a script that offsets a transforms position and rotation - but I don’t want to “destroy” the original position and rotation values of the object.

I have considered two approaches:

1. As above, I use Translate and Rotate to rotate the object to a new location, and next update I subtract the Translate and Rotate values. The problem with this method is the precision of rotation, I can’t seem to invert the rotation accurately.

2. Caching the position and rotation in Update() then updating the position and rotation directly in LateUpdate(). The problem with this approach is that since there is no EarlyUpdate() function I can’t guarantee that the position and rotation is cached before any other script may alter the position/rotation of the object.

Any suggestions how to achieve offset position/rotation without affecting the original position/rotation of an object?

You can’t expect absolute precision, but the problem in your case is actually the order the rotations are applied. When you use Euler angles (as Transform.Rotate does), if you consider them to be rotations around local axes (the default), it’s equivalent to performing a Y axis rotation, then an X axis rotation, then a Z axis rotation. (The documentation lists them in the opposite order, but that’s only correct for the non-default case where you’re performing each rotation about the world axes, or some other frame that doesn’t rotate with the object.)

``````transform.Rotate( new Vector3(1,1,0));
``````

is the same as this (plus rounding error):

``````transform.Rotate( new Vector3(0,1,0));
transform.Rotate( new Vector3(1,0,0));
``````

and in order to invert it, you have to apply the backwards rotations in the opposite order, like this:

``````transform.Rotate( new Vector3(-1,0,0));
transform.Rotate( new Vector3(0,-1,0));
``````

However, your second line of code is equivalent to these two lines but the other way around, and that’s why you don’t end up with the orientation you expect. It will be much more obvious with larger angles, too.

So one solution is to split the axis rotations up, at least when you’re doing the backwards rotation, and apply them individually in the correct order. Another is to calculate the quaternion you’re applying separately from applying it, remember it, and calculate its inverse before applying that. This is quite straightforward:

``````myRotationDelta = Quaternion.Euler(1, 1, 0);
transform.localRotation *= myRotationDelta;
... later ...
transform.localRotation *= Quaternion.Inverse(myRotationDelta);
``````

I don’t know whether this helps much with your problem though. If somebody changes the position or rotation while you have it modified, and you then try to undo your modification, you won’t generally end up with the same result you’d get if the external rotation was applied without your rotation being there at all.

Presumably you only want to apply the offset for rendering? If so, maybe you can apply the rotation in LateUpdate (perhaps using script execution order to ensure your script runs last), and revert your rotation at the point of WaitForEndOfFrame (which you’ll have to do in a coroutine).

Thank you very much for the excellent reply George. It makes total sense (of course) and I was too mentally blind to realize the rotational order made an impact. You also predicted correctly the next problem I’d run in to.

Just rendering the offset is what I’m after at this stage and WaitForEndOfFrame is a savior. I was hoping for an EarlyUpdate(), but the WaitForEndOfFrame coroutine seems to work.

Thank you once again.