- 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