Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.

Per-Axis (Euler) Rotation Control While Avoiding Gimbal Lock?

Discussion in 'Scripting' started by S_Darkwell, Oct 15, 2014.

  1. S_Darkwell

    S_Darkwell

    Joined:
    Oct 20, 2013
    Posts:
    320
    If anyone here would be able to assist, I would be most gracious. I posted this on Unity Answers almost 3 days ago. Thus far, it has garnered 42 views, but no replies as of yet. It is rather embarrassing how many hours I've put into trying to solve this puzzle.

    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:

    Code (CSharp):
    1. privatevoidRotate()
    2. {
    3.    var _target =Quaternion.LookRotation(Target.position - transform.position,Target.up).eulerAngles;
    4.    var _next = transform.eulerAngles;
    5.  
    6.    for(var i =0; i <3; i++)
    7.       _next[i]=Mathf.LerpAngle(_next[i], _target[i], smoothRotateSpeed[i]*Time.deltaTime);
    8.  
    9.    transform.localRotation =Quaternion.Euler(_next);
    10. }
    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?
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,405
    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.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class GimbalTest : MonoBehaviour {
    6.  
    7.     public Transform _target;
    8.     public Vector3 smoothRotateSpeed;
    9.  
    10.     // Use this for initialization
    11.     void Start ()
    12.     {
    13.    
    14.     }
    15.    
    16.     // Update is called once per frame
    17.     void Update ()
    18.     {
    19.         //var target = Quaternion.LookRotation(_target.position - this.transform.position, _target.up).eulerAngles;
    20.         //var next = transform.eulerAngles;
    21.  
    22.         //for (var i = 0; i < 3; i++)
    23.         //    next[i] = Mathf.LerpAngle(next[i], target[i], smoothRotateSpeed[i] * Time.deltaTime);
    24.  
    25.         //transform.localRotation = Quaternion.Euler(next);
    26.  
    27.         var targetRot = Quaternion.LookRotation(_target.position - this.transform.position, _target.up);
    28.         var delta = FromToRotation(this.transform.rotation, targetRot);
    29.         var deltaEuler = delta.eulerAngles;
    30.  
    31.         for (var i = 0; i < 3; i++)
    32.             deltaEuler[i] = Mathf.LerpAngle(0f, deltaEuler[i], smoothRotateSpeed[i] * Time.deltaTime);
    33.  
    34.         transform.rotation *= Quaternion.Euler(deltaEuler);
    35.     }
    36.  
    37.  
    38.     public static Quaternion FromToRotation(Quaternion start, Quaternion end)
    39.     {
    40.         return Quaternion.Inverse(start) * end;
    41.     }
    42. }
    43.  
     
    Last edited: Oct 15, 2014
    S_Darkwell likes this.
  3. S_Darkwell

    S_Darkwell

    Joined:
    Oct 20, 2013
    Posts:
    320
    @lordofduct: 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.

    Firstly, are you on unityAnswers? This is the URL for the original question: (http://answers.unity3d.com/questions/807996/per-axis-euler-rotation-control-while-avoiding-gim.html). If you wished to post your answer to said question there, I would gladly accept it outright.

    Secondly, your QuaternionUtil class sounds fantastically useful. Would there be any potential that you would be willing to share such a class?

     
  4. S_Darkwell

    S_Darkwell

    Joined:
    Oct 20, 2013
    Posts:
    320
    @lordofduct:

    Also, as a general observation, I noticed the use of "this.transform" is quite common, as opposed to "transform." This has always struck me as redundant, and Visual Studio's Resharper plugin also marks it as thus. Is this merely stylistic or a holdover from another language / application, or is there a practical benefit to "this"?
     
  5. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,405
    I think I do, I'll go do that.

    I don't have the whole class available online, but a version with a few methods on it.

    https://code.google.com/p/spacepupp...cepuppyUnityFramework/Utils/QuaternionUtil.cs

    There's other classes in that source that may be useful. We only release some of the framework online. Also, a lot of the stuff on there is young (I'm building the framework right now), so things change a lot as I iron out kinks in it.

    I did not know that resharper flags any uses of "this" by default. I find that really weird.

    There's no real difference operation wise. I can't remember exactly, but I'm pretty sure it compiles into the same code either way (with some exceptions where the this keyword is distinguishing between two variables of the same name that exist in the same scope, in which case the 'this' keyword is necessary to distinguish between the two).

    I do find it practical though as it makes things readable for me. I have certain standards I follow for myself. When I see "this." I know I'm accessing the local space.

    Lets say for instance I had a class out there called 'Target'. How would I know the difference between accessing a property named 'Target' or the class named 'Target'. If I use the 'this' keyword, I know which I'm referring to (also this lets the compiler know).

    My standard is that all classes start with an uppercase letter. Over the years of programming I've trained myself to recognize this as such. So when I see an uppercase letter being accessed as a property... my brain immediately thinks it is a static method on a class. Of course, if I look deeper into it I can figure it out, but that can take a lot of time (a minute is a lot of time while you're working)... but it doesn't let me quick read. The 'this' keyword instead allows me to mentally parse it much more quickly.

    This is especially useful when posting stuff on forums, where we don't have an IDE to easily inform us of stuff by mousing over various elements. I need to convey this information as compactly as possible. And most experienced developers can recognize (even if they don't agree with) my standards and parse my code easily as well.



    Oh funny thing. DotPeek, a decompiler tool by 'JetBrains', the makers of ReSharper... When you decompile code with DotPeek it injects 'this' lines even when you haven't put a 'this' statement. I'm pretty sure it's because the .Net compiler is injecting the 'this' statements... and DotPeek is just picking those up. What I can say though is that it's counter to their standard in ReSharper.
     
  6. S_Darkwell

    S_Darkwell

    Joined:
    Oct 20, 2013
    Posts:
    320
    @lordofduct:


    Your answer has been upvoted! Hey, now you won’t have to wait for your questions / answers to be approved!

    Duly noted regarding the transient nature of your current code. Thank you so much for sharing! I’ll definitely take a look through it for inspiration.


    I like your logic! I’ll have to consider implementing this in my own code. That is odd! “this” is certainly marked as redundant by default.


    I’ve also taken a peek at your blog. I’m most certainly going to follow it. It seems intriguing.


    Thank you again for everything!