- Home /
 
Per-Axis (Euler) Rotation Control While Avoiding Gimbal Lock
I am writing a target-following script with numerous per-axis settings. Below is the most distilled example I can provide of the rotation portion:
 private void Rotate()
 {
     var _target = Quaternion.LookRotation(Target.position - transform.position, Target.up).eulerAngles;
     var _next = transform.eulerAngles;
 
     for (var i = 0; i < 3; i++)
         _next[i] = Mathf.LerpAngle(_next[i], _target[i], smoothRotateSpeed[i] * Time.deltaTime);
 
     transform.localRotation = Quaternion.Euler(_next);
 }
 
               In the example above, "smoothRotationSpeed" is a Vector3 that applies a different smoothing value to each axis.
Unfortunately, because I am modifying each axis individually, I am running into Gimbal lock issues. How can one avoid or compensate for Gimbal lock without losing per-axis (euler) control?
Answer by lordofduct · Oct 15, 2014 at 09:42 PM
My answer from: http://forum.unity3d.com/threads/per-axis-euler-rotation-control-while-avoiding-gimbal-lock.274050/#post-1809878
I can't guarantee this will work. But give it a try. I basically only deal with the euler angles of the rotation between current and target. I adjust how much impact that rotation has, and bring it back to quaternions. Dealing with quats as much as possible.
I generalized the 'FromToRotation' as its own static method. That's because I have my own "QuaternionUtil" class that contains a lot of methods likes this. I'm really annoyed by the lack of methods built into the Quaternion from Unity. They have a FromToRotation to get a quaternion between two vectors, but not between two quaternions. For shame.
Note I also renamed the variables.
 using UnityEngine;
 using System.Collections;
  
 public class GimbalTest : MonoBehaviour {
  
     public Transform _target;
     public Vector3 smoothRotateSpeed;
  
     // Use this for initialization
     void Start ()
     {
    
     }
    
     // Update is called once per frame
     void Update ()
     {
         //var target = Quaternion.LookRotation(_target.position - this.transform.position, _target.up).eulerAngles;
         //var next = transform.eulerAngles;
  
         //for (var i = 0; i < 3; i++)
         //    next[i] = Mathf.LerpAngle(next[i], target[i], smoothRotateSpeed[i] * Time.deltaTime);
  
         //transform.localRotation = Quaternion.Euler(next);
  
         var targetRot = Quaternion.LookRotation(_target.position - this.transform.position, _target.up);
         var delta = FromToRotation(this.transform.rotation, targetRot);
         var deltaEuler = delta.eulerAngles;
  
         for (var i = 0; i < 3; i++)
             deltaEuler[i] = Mathf.LerpAngle(0f, deltaEuler[i], smoothRotateSpeed[i] * Time.deltaTime);
  
         transform.rotation *= Quaternion.Euler(deltaEuler);
     }
  
  
     public static Quaternion FromToRotation(Quaternion start, Quaternion end)
     {
         return Quaternion.Inverse(start) * end;
     }
 }
  
 
              Your solution worked perfectly. It was practically drag-and-drop! Pasted it into its place, changed _target to Target, and it worked straight away. I truly cannot thank you enough! The elegance and simplicity of your solution certainly illustrates my lack of knowledge about Quaternions.
Your answer
 
             Follow this Question
Related Questions
Rotation Lerp 2 Answers
Rotating on a plane 2 Answers
Rotation of child object is different in play mode 0 Answers
Incrementing Rotation Produces Strange Transform Values 1 Answer
Make Quaternion Slerp happen over a 1 second period 3 Answers