Search Unity

Quaternios, Eulers and Gimbal Lock

Discussion in 'Scripting' started by pretender, Oct 24, 2019.

  1. pretender

    pretender

    Joined:
    Mar 6, 2010
    Posts:
    865
    Hi guys, I have interesting problem I am trying to solve. I want to have three input boxes to enter angles for rotations of object separately for each axis, for example entering 30 in x-axis will rotate object 30 degrees around x-axis, similiar for other axis, the problem arises when using angles over 90 degrees because of the gimbal lock. Later you can't know what should be rotated, any idea how to track this? Using Quaternions changes values for each axis while rotating around one, so i can't use it. Here is the code when using Eulers that work good except the gimbal lock...clicking on buttons just rotates by 10 degrees and with _currentEdit i remember what is the current rotation so i can used it for next one, rotation is reset before setting new one:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using Sirenix.OdinInspector;
    4. using UnityEngine;
    5.  
    6. public class RotationController : MonoBehaviour
    7. {
    8.     private Transform _child;
    9.     private Quaternion _currentRotation;
    10.     private Vector3 _currentEdit;
    11.  
    12.     void Awake()
    13.     {
    14.         _child = GetComponent<Transform>();
    15.  
    16.         if(_child == null) Debug.Log("Child is null");
    17.     }
    18.  
    19.     public int GetRandomInt => UnityEngine.Random.Range(0, 100);
    20.  
    21.     [Button(ButtonSizes.Large)]
    22.     public void RotateChildByXIncrease()
    23.     {
    24.         _currentEdit = new Vector3(_currentEdit.x + 10, _currentEdit.y, _currentEdit.z);
    25.  
    26.         _child.rotation = Quaternion.identity;
    27.         _child.rotation = Quaternion.Euler(_currentEdit);
    28.  
    29.         PrintAngles();
    30.     }
    31.  
    32.     [Button(ButtonSizes.Large)]
    33.     public void RotateChildByXDecrease()
    34.     {
    35.         _currentEdit = new Vector3(_currentEdit.x - 10f, _currentEdit.y, _currentEdit.z);
    36.  
    37.         _child.rotation = Quaternion.identity;
    38.         _child.rotation = Quaternion.Euler(_currentEdit);
    39.  
    40.         PrintAngles();
    41.     }
    42.  
    43.     [Button(ButtonSizes.Large)]
    44.     public void RotateChildByYIncrease()
    45.     {
    46.         _currentEdit = new Vector3(_currentEdit.x, _currentEdit.y + 10f, _currentEdit.z);
    47.  
    48.         _child.rotation = Quaternion.identity;
    49.         _child.rotation = Quaternion.Euler(_currentEdit);
    50.  
    51.         PrintAngles();
    52.     }
    53.  
    54.     [Button(ButtonSizes.Large)]
    55.     public void RotateChildByYDecrease()
    56.     {
    57.         _currentEdit = new Vector3(_currentEdit.x, _currentEdit.y - 10f, _currentEdit.z);
    58.  
    59.         _child.rotation = Quaternion.identity;
    60.         _child.rotation = Quaternion.Euler(_currentEdit);
    61.  
    62.         PrintAngles();
    63.     }
    64.  
    65.     [Button(ButtonSizes.Large)]
    66.     public void RotateChildByZIncrease()
    67.     {
    68.         _currentEdit = new Vector3(_currentEdit.x, _currentEdit.y, _currentEdit.z + 10f);
    69.  
    70.         _child.rotation = Quaternion.identity;
    71.         _child.rotation = Quaternion.Euler(_currentEdit);
    72.  
    73.         PrintAngles();
    74.     }
    75.  
    76.     [Button(ButtonSizes.Large)]
    77.     public void RotateChildByZDecrease()
    78.     {
    79.         _currentEdit = new Vector3(_currentEdit.x, _currentEdit.y, _currentEdit.z - 10f);
    80.  
    81.         _child.rotation = Quaternion.identity;
    82.         _child.rotation = Quaternion.Euler(_currentEdit);
    83.  
    84.         PrintAngles();
    85.     }
    86.  
    87.     public void PrintAngles()
    88.     {
    89.         Debug.Log("Rotation: " + _child.rotation.eulerAngles + "|" + _currentEdit);
    90.     }
    91. }
    92.  
    93.  
     
  2. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,605
    Not an expert here, but i believe i'm right when i say that what you want is not possible. Gimbal lock is one of the inherit problems with euler angles, and the reason quaternions are used internally. Basically, what you want is exactly what the unity editor (inspector) already does: keep track of the euler inputs of the user. However, the unity editor mode (when inputting values through the inspector) has the exact same problem with that: gimbal lock. If there was a workaround, Unity would have implemented it.
    The main reason the inspector does it like it does, is that it makes manually inputting values much easier. If the input values were converted to quaternions internally and then back to euler angles for readability, then this may result in the output values jumping all over the place. This is because Quaternion->Euler has multiple representations iirc.

    So you have the choice between hard to understand but precice and problem free representations, vs. easy to understand but problematic representations for your rotations.

    One trade-off, which may or may not fit for your situation would be this:
    You could have 3 input boxes, and 3 read-only output ui elements. Internally, you work with a quaternion which gets recalculated based on user inputs. Instead of rotating to fit the input rotation, you rotate by the input values; so when the user inputs 30,0,0, you rotate by 30° (more) on the x-axis. After the user accepts / applies the change, the input boxes get reset to 0. The change gets applied to the underlying quaternion representation, and afterwards you convert Quaternion->Euler to get the read-only ui output.
    This would allow the user to input euler rotations, free of gimbal lock, as well as having euler outputs that accurately describe the current rotation. However, while always correct, the output euler values may not reflect the old values plus the changes done by the user, which may or may not be confusing in your situation.

    As far as i'm aware, those are your only options. If i missed something i'm sure somebody will correct me.
    Hope this helps :)
     
    pretender likes this.
  3. pretender

    pretender

    Joined:
    Mar 6, 2010
    Posts:
    865
    Thank you!