0
\$\begingroup\$

I have an object which i want to rotate relative to world space. I have three variables (one for every axis) which store the rotation. By pressing buttons on my controller I add or subtract 90 to these variables (rotX, rotY, rotZ). Then I rotate my Object like this:

transform.Rotate (rotX * delta, rotY * delta, rotZ * delta, Space.World);

As this is called every frame the object would spin forever so i subtract the ammount I've allready rotated afterwards:

rotX -= rotX * delta;
rotY -= rotY * delta;
rotZ -= rotZ * delta;

This works fine as long as I don't rotate two axis at once. Then the object rotates rotates only partially to the desired position (e.g. 83 if I want to rotate to 90).

I can't see what I've done wrong. It actually seems like the moment I start an rotation around an axis all other axis get canceled. But if I monitor the variables they look exactly as they should.

Thank you in advance and have a amazing day!

\$\endgroup\$
1
\$\begingroup\$

Rotations don't work the same as translations. They're order-dependent and their axes are not globally orthogonal - a small rotation on one axis begins to affect the results of subsequent rotations, even when you're compounding rotations in world space (since the orientation of the object relative to the rotation axis changes). See this answer for some animated examples.

To fix this, it often works better to separate your destination from your current rotation. Then you can change your destination in discrete steps, while your current rotation plays catch-up at a controllable speed. As a bonus, this also makes it easier to introduce easing curves for a smoother/juicier feel. :)

// Measured in turns per second for ease of use.
public float rotationSpeed = 1f;

Quaternion _targetRotation = Quaternion.identity;    

void Update() {
   if(Input.GetButtonDown("PitchPlus"))
     _targetRotation = Quaternion.Euler(90, 0, 0) * _targetRotation;
   if(Input.GetButtonDown("PitchMinus"))
     _targetRotation = Quaternion.Euler(-90, 0, 0) * _targetRotation;
   if(Input.GetButtonDown("YawPlus"))
     _targetRotation = Quaternion.Euler(0, 90, 0) * _targetRotation;
   if(Input.GetButtonDown("YawMinus"))
     _targetRotation = Quaternion.Euler(0, -90, 0) * _targetRotation;
   if(Input.GetButtonDown("RollPlus"))
     _targetRotation = Quaternion.Euler(0, 0, 90) * _targetRotation;
   if(Input.GetButtonDown("RollMinus"))
     _targetRotation = Quaternion.Euler(0, 0, -90) * _targetRotation;

   transform.rotation = Quaternion.RotateTowards(
                          transform.rotation,
                          _targetRotation,
                          rotationSpeed * 360f * Time.deltaTime);       
}

This could hypothetically still accumulate small errors over many rotations due to floating point rounding (though with pure 90-degree rotations it should be OK as long as Unity's internal methods are numerically stable). If that turns out to be a problem, you can replace the _targetRotation with something like an orientation struct that models just the 24 orientations you care about.

\$\endgroup\$
  • \$\begingroup\$ Thank you so much! It now not only works, it also seems like a way better solution to what i want to achieve. The floating point operations aren't a problem. After 20 rotations the object's rotation is only off by .000001 which is enough accuracy for me. \$\endgroup\$ – xeetsh Sep 8 '17 at 8:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.