Search Unity

How to get Euler rotation angles exactly as displayed in the Transform inspector? [SOLVED]

Discussion in 'Scripting' started by Steven-Walker, Aug 10, 2016.

  1. Steven-Walker

    Steven-Walker

    Joined:
    Oct 27, 2010
    Posts:
    38
    This is an age old problem and I know there are tons of posts on using quaternions for rotation, and I understand gimbal lock, however I am still missing something...

    What I want to do is read the current Euler angles for a transform, however the values I am getting do not match what I see in the inspector. Whatever Unity is doing in the Transform inspector to display the (Euler) rotation values, I want to match that. I understand that under the hood rotations are Quaternions, but how is this value then converted to the Euler display? Reading the eulerAngles value isn't cutting it.

    For example, when I manually input a rotation in the inspector of [180,0,0], I expect that when I read transform.localEulerAngles (or transform.localRotation.eulerAngles) that I will also get [180,0,0], however instead what I am getting is [0,180,180]. Furthermore, the rotations wrap around from 0-360, so if the inspector shows a value such as [0,720,0], the value I get from transform.localEulerAngles is [0,0,0]. Unity is obviously calculating these values in a different way.

    So the big question is: how do I get the exact same Euler rotation values shown in the inspector?

    Here is a very simple script that demonstrates the problem, and also a screenshot attached showing the object and console output.
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. [ExecuteInEditMode()]
    5. public class RotationTest : MonoBehaviour {
    6.  
    7.     void Update () {
    8.         Debug.Log("Q:"+transform.localRotation+" E1:"+transform.localRotation.eulerAngles+" E2:"+transform.localEulerAngles);
    9.     }
    10. }
    11.  
    I'm hoping this is a simple fix. Appreciate you taking the time to read and for any help.

    Thanks,
    Stephen
     

    Attached Files:

  2. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    The euler angles displayed in the inspector concern the local rotation. That's how you can access them:
    Code (CSharp):
    1. transform.localRotation.eulerAngles
     
    Last edited: Aug 10, 2016
    sean244 likes this.
  3. zaxvax

    zaxvax

    Joined:
    Jun 9, 2012
    Posts:
    220
    I think Stephen was asking about accessing fields in editor.
    eulerAngles will return (0,300,0) no matter what, while editor will keep the exact value it accepted, like (0,300,0), (0,-60,0), (0,-420,0) and so on.
    This kind of behavior.
     
  4. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,188
    If I remember correctly. EulerAngles allows you by code to do something like spin around 720 and it will spin twice, however if you check the value you get from eulerAngles after, it will return 0. One of my work projects involved a prize wheel and you had to be careful because code wise you always got a number from 0-360, but I think in the inspector it just kept going up and up, more like it's adding degrees.

    As far as getting the exact value they display, don't know. Never had a use case for it honestly. But would be interesting if someone knew a way.
     
  5. Steven-Walker

    Steven-Walker

    Joined:
    Oct 27, 2010
    Posts:
    38
    transform.localRotation.eulerAngles is not returning the same value as displayed in the inspector, so it doesn't solve my problem. Somehow the inspector is showing extended Euler angles that go well beyond 0-360, and maintain those values consistently – if it didn't then entering [180,0,0] would immediately change to [0,180,180], but that's not what happens. There is additional math (or data) that is not being represented by transform.localRotation.eulerAngles.

    Also to clarify, I care more about the calculation/data than the inspector. The inspector window after all is just the GUI. I want to know how/where it gets the values it displays. If the rotation is purely stored internally as Quaternion, then how do I extract the extended Euler angle values to exactly match what I see in the inspector? Or if there is additional data being stored, how do I access that?
     
    CloudyVR and sean244 like this.
  6. zaxvax

    zaxvax

    Joined:
    Jun 9, 2012
    Posts:
    220
    Well, when you edit your object in editor (I mean rotate) it will save additional data. All field of your objects are serialized and stored in the scene file. If you force Unity to keep em human readable aka text format, you can see something like this:

    Transform:
    m_ObjectHideFlags: 0
    m_PrefabParentObject: {fileID: 0}
    m_PrefabInternal: {fileID: 0}
    m_GameObject: {fileID: 1165063687}
    m_LocalRotation: {x: -0, y: 0.9425249, z: -0, w: -0.3341359}
    m_LocalPosition: {x: 0, y: 0, z: 0}
    m_LocalScale: {x: 1, y: 1, z: 1}
    m_LocalEulerAnglesHint: {x: 0, y: -500.96, z: 0}
    m_Children: []
    m_Father: {fileID: 0}
    m_RootOrder: 0
    But eulerAngles will return you the same value within 0 to 360 range.
    However if you will rotate object in script, unity will keep values in editor within -180 to 180 range.

    How to calculate it? Simple. Add or remove 360 until it's within desirable range. (not exactly simple, but you can figure it out of course, you've got the idea I hope :) )
     
    GamerLordMat likes this.
  7. Steven-Walker

    Steven-Walker

    Joined:
    Oct 27, 2010
    Posts:
    38
    It's interesting there's the field m_LocalEulerAnglesHint, although that data isn't accessible through scripting as far as I can find. Without that info it's impossible to know how much to add/subtract 180 to find the desired range. Looks like this is a serious limitation in working with rotations. Appreciate your help, though still looking for a solution that will work consistently and isn't overly cumbersome. Hopeful that there is still something I have missed.
     
    frankrs and ericbegue like this.
  8. zaxvax

    zaxvax

    Joined:
    Jun 9, 2012
    Posts:
    220
    Not really a limitation, just a very annoying feature when you design your scene in editor.

    Simplest solution would be to add your own rotation controls in inspector.
     
  9. passerbycmc

    passerbycmc

    Joined:
    Feb 12, 2015
    Posts:
    1,741
    if it is really that needed you can get m_LocalEulerAnglesHint via reflection
     
  10. Steven-Walker

    Steven-Walker

    Joined:
    Oct 27, 2010
    Posts:
    38
    Thanks everyone for helping me figure this out. I've used reflection to find all properties of UnityEngine.Transform, but m_LocalEulerAnglesHint does not exist. Unless it is not a property of Transform? Here is a complete list of PropertyInfo (there is nothing for FieldInfo):

    Code (CSharp):
    1.  
    2. position (UnityEngine.Vector3)
    3. localPosition (UnityEngine.Vector3)
    4. eulerAngles (UnityEngine.Vector3)
    5. localEulerAngles (UnityEngine.Vector3)
    6. right (UnityEngine.Vector3)
    7. up (UnityEngine.Vector3)
    8. forward (UnityEngine.Vector3)
    9. localScale (UnityEngine.Vector3)
    10. childCount (System.Int32)
    11. lossyScale (UnityEngine.Vector3)
    12. hasChanged (System.Boolean)
    13. hierarchyCapacity (System.Int32)
    14. hierarchyCount (System.Int32)
    15. gameObject (UnityEngine.GameObject)
    16. tag (System.String)
    17. rigidbody (UnityEngine.Component)
    18. rigidbody2D (UnityEngine.Component)
    19. camera (UnityEngine.Component)
    20. light (UnityEngine.Component)
    21. animation (UnityEngine.Component)
    22. constantForce (UnityEngine.Component)
    23. renderer (UnityEngine.Component)
    24. audio (UnityEngine.Component)
    25. guiText (UnityEngine.Component)
    26. networkView (UnityEngine.Component)
    27. guiElement (UnityEngine.Component)
    28. guiTexture (UnityEngine.Component)
    29. collider (UnityEngine.Component)
    30. collider2D (UnityEngine.Component)
    31. hingeJoint (UnityEngine.Component)
    32. particleEmitter (UnityEngine.Component)
    33. particleSystem (UnityEngine.Component)
    34. name (System.String)
    35. hideFlags (UnityEngine.HideFlags)
    36.  
    It seems obvious I need to rethink my approach to this problem, since there appears to be no way to unambiguously convert Quaternion to Euler angles.

    My (incorrect) assumption was that I would be able to read and write the X, Y, and Z angles separately in different passes (across different scripts), but Quaternions represent a final total rotation, and there's no way to know the original input Euler values from the Quaternion alone. Ultimately I am using this for animation, so I may have cases where one component controls the X rotation and another controls the Y rotation. But with the current state of things, either one overwrites the other, or I get flipping values since reading the Euler angles resolve to an equivalent but wrong rotation for interpolation. This approach actually works fine for Y and Z rotations, but X causes flipping. Quaternion interpolation isn't the solution either because it always comes back to knowing the original input values entered by the user in Euler angles.

    My next approach is to try creating a component that acts as a middleman that handles getting/setting rotation via Euler angles and storing the original values to retain context. This will only work with scripts I write to use it, but it's the only workaround I can think of to retain the original Euler values.
     
  11. passerbycmc

    passerbycmc

    Joined:
    Feb 12, 2015
    Posts:
    1,741
    it might be only be part of the editor script for it, and thus be in the SerializedObject of transform
     
  12. zaxvax

    zaxvax

    Joined:
    Jun 9, 2012
    Posts:
    220
    I don't really see why both approaches should not work. While last one is much much cleaner, first one must be perfectly fine in small game.
    In your first approach try to not calculate angles yourself. Just command your object to turn around specific axis by specific amount of degrees.
    In last approach you can collect data for your calculation from hundreds of sources and still be able to debug.
     
  13. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    As an alternative to accessing Unity internals, you could write your own script to manage objects that require angles of that range:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. [ExecuteInEditMode]
    4. public class MyRotation : MonoBehaviour
    5. {
    6.     public Vector3 rotation;
    7.  
    8.     void Update ()
    9.     {
    10.         transform.localRotation = Quaternion.Euler(rotation);
    11.     }
    12. }
    13.  
    Edit:
    @Steven-Walker
    Just reading that you've already consider this approach in your previous post.:p
     
    Last edited: Aug 11, 2016
  14. Steven-Walker

    Steven-Walker

    Joined:
    Oct 27, 2010
    Posts:
    38
    So to follow up, I finished implementing the solution as described by @ericbegue and it is working great! In the end it was pretty easy to set up. The only caveat is if any other scripts on the same object set localRotation, then those values will be overwritten. To help ensure my script sets the value in the end, I am setting it in LateUpdate.

    To make other scripts work with the new rotation component, they just need to look for, or create the component. The code looks something like this:

    Code (CSharp):
    1. if(myRotation == null) {
    2.     myRotation = gameObject.GetComponent<Rotation>();
    3.     if(myRotation == null) {
    4.         myRotation = gameObject.AddComponent<Rotation>();
    5.     }
    6. }
    7. if(myRotation != null) {
    8.     myRotation.Euler.x = xValue; // or whatever
    9. }
    10.  
    Since the Euler values persist, it allows single axis manipulation without affecting the other axis, and the values are not constrained 0 to 360.
     
  15. javinair

    javinair

    Joined:
    Jun 26, 2015
    Posts:
    1
    Hello @Steven-Walker, I would like to know how is being used your script with @ericbegue 's one. I've written your post into a new script, and this script refers to MyRotation (ericbegue's), but yours refers to an Euler property that his script doesn't have.

    Could you suggest me how to use your solution?
    Thanks in advance.
     
  16. Techno_Anarchist

    Techno_Anarchist

    Joined:
    Oct 12, 2017
    Posts:
    1

    Ive been having the same problem, and im trying to get your code in your latest post to work, could you show the rest of the file so i can see how you are using these variables?

    Thanxs, its been very troubling to solve this, hopefully this will work
     
  17. tinyant

    tinyant

    Joined:
    Aug 28, 2015
    Posts:
    127
    I think you should use C# reflection to get the transform inspector rotation value.
    it's just a Vector3Field for temp vector3 value.
    so you should get your selected GameObject and get the TransformInspector instance and to get the Temp field (Vector3) in the TransformInspector.
     
    gglobensky likes this.
  18. gglobensky

    gglobensky

    Joined:
    May 15, 2017
    Posts:
    2
    Found on Reddit and tested :

    SerializedObject serializedObject = new UnityEditor.SerializedObject(transform);
    SerializedProperty serializedEulerHint = serializedObject.FindProperty("m_LocalEulerAnglesHint");
    Debug.Log(serializedEulerHint.vector3Value);
     
    Barliesque and ovrdb like this.
  19. rnner9000

    rnner9000

    Joined:
    Sep 6, 2020
    Posts:
    8
  20. Mohak_dev

    Mohak_dev

    Joined:
    Sep 24, 2021
    Posts:
    7
    I tried something for myself and I can confirm it works

    Code (CSharp):
    1.     float GetObjectRotation()
    2.     {
    3.         if(transform.eulerAngles.z > 180)
    4.         {
    5.             return transform.eulerAngles.z - 360;
    6.         }
    7.         else
    8.         {
    9.             return transform.eulerAngles.z;
    10.         }
    11.     }
    If the angle is greater than 180 than it subtracts 360 from it to display a negative value