Search Unity

[SOLVED] How to get "Rotation" value that is in the inspector?

Discussion in 'Scripting' started by AlanMattano, Mar 10, 2017.

Thread Status:
Not open for further replies.
  1. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,998
    Except it doesn't work. The words from so many people explain why.
     
    vonSchlank likes this.
  2. ShamimAkhter

    ShamimAkhter

    Joined:
    Jun 21, 2019
    Posts:
    2
    I'm working on a flight simulator project. With this script, I can easily find the Roll/Bank, Pitch/AngleOfAttack and Yaw/Heading of my aircraft. So it's working fine. I would like to know what's the problem it's showing in your case.
     
    Deleted User likes this.
  3. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,998
    All that code does is convert eulerAngles from 0-360 into -180 to 180. That's easy, isn't the question, and there were shorter answers when this was posted.

    The Q was about how a rotation of 270 might be converted into -90 for the Inspector, or it might not. It might even be shown as 630 or -450. But a script will always see it as 270. How can a script see the same numbers as the human?
     
  4. Deleted User

    Deleted User

    Guest

    It cannot be. Unless you are using any editor extension to grab that value directly from the inspector and in that case, it won't help after the build. Personally I don't find any need for those 630 or -450 degrees values ever.
     
    SpaceManDan likes this.
  5. itisieric

    itisieric

    Joined:
    Jul 15, 2018
    Posts:
    4
    This should be what you're looking for but i think you need an up to date version of unity first

    1. public float x;
    2. public float y;
    3. public float z;
    4. x = (UnityEditor.TransformUtils.GetInspectorRotation(getRotation.transform).x) + offSetX1;
    5. y = (UnityEditor.TransformUtils.GetInspectorRotation(getRotation.transform).y) + offSetY1;
    6. z = (UnityEditor.TransformUtils.GetInspectorRotation(getRotation.transform).z) + offSetZ1;
    7. Debug.Log("x: " + x);
    8. Debug.Log("y: " + y);
    9. Debug.Log("z: " + z);
     
  6. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,998
    TinyAnt suggested using TransformUtils.GetInspectorRotation a year and a half ago. The problem is it's editor-only. People are wanting this for a build (I can't figure out why -- they just want it).
     
  7. Cherubim79

    Cherubim79

    Joined:
    May 3, 2014
    Posts:
    56
    TL;DR: Here's some new math that works at runtime with or without the editor, I'm just explaining it, and sharing some new linear algebra finds.

    I managed to find a better solution to this, but it's a little more math heavy and I discovered some new math by trial and error along the way. I read a lot of technical whitepapers from universities, and even found along the way that you can recreate gimbal lock with Quaternions if you know what you're doing with pre or post multiplying quaternions and matrices, I'll get into that topic more in a minute.

    Here I've created an "Angular Distance" formula, which takes two Euler angles and compared by the cosines and sines of the angles.

    Code (CSharp):
    1.     float AngularDistance(Vector3 a, Vector3 b)
    2.     {
    3.         float cax, cay, caz, sax, say, saz, cbx, cby, cbz, sbx, sby, sbz;
    4.  
    5.         cax = Mathf.Cos(a.x * Mathf.Deg2Rad);
    6.         cay = Mathf.Cos(a.y * Mathf.Deg2Rad);
    7.         caz = Mathf.Cos(a.z * Mathf.Deg2Rad);
    8.         cbx = Mathf.Cos(b.x * Mathf.Deg2Rad);
    9.         cby = Mathf.Cos(b.y * Mathf.Deg2Rad);
    10.         cbz = Mathf.Cos(b.z * Mathf.Deg2Rad);
    11.         sax = Mathf.Sin(a.x * Mathf.Deg2Rad);
    12.         say = Mathf.Sin(a.y * Mathf.Deg2Rad);
    13.         saz = Mathf.Sin(a.z * Mathf.Deg2Rad);
    14.         sbx = Mathf.Sin(b.x * Mathf.Deg2Rad);
    15.         sby = Mathf.Sin(b.y * Mathf.Deg2Rad);
    16.         sbz = Mathf.Sin(b.z * Mathf.Deg2Rad);
    17.  
    18.         float ct1 = cbx - cax;
    19.         float ct2 = cby - cay;
    20.         float ct3 = cbz - caz;
    21.         float st1 = sbx - sax;
    22.         float st2 = sby - say;
    23.         float st3 = sbz - saz;
    24.  
    25.         return Mathf.Sqrt(ct1*ct1 + ct2*ct2 + ct3*ct3 + st1*st1 + st2*st2 + st3*st3);
    26.     }
    27.  
    Remember that except in the case of gimbal lock at poles where the pitch is +/- 90 degrees, you will usually retrieve two Euler angles per matrix or quaternion, which are isomorphic to each other. In the code below I obtain both angles with their respective plus or minus cosine of the pitch angle (in Unity's case the X axis) and compare to find the angle with the lowest distance, I then constrain it to within 0-360 degrees with modulo operations.

    This is a quick and simple proof of concept I coded up tonight so it doesn't have gimbal lock checking, which is fairly trivial to code, I have a better version I've been working on in ThreeJS.

    What Unity does by default is just picks the Euler angle that contains the positive cosine of the pitch angle.

    It's more efficient, but it can be very annoying if you were trying to read Euler angles that were constantly flip-flopping between positive and negative, obviously the discussion of this topic.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Rotations : MonoBehaviour
    6. {
    7.     public float Amount = 1.0f;
    8.     public bool Premultiply = true;
    9.     public Vector3 Axis = Vector3.zero;
    10.  
    11.     public Vector3 LastEuler = Vector3.zero;
    12.     public Quaternion Quat = Quaternion.identity;
    13.     public Vector3 CurrentEuler = Vector3.zero;
    14.     public Matrix4x4 M1 = Matrix4x4.identity;
    15.     public Vector3 UnityCurrentEulerChoice = Vector3.zero;
    16.  
    17.     public Vector3 AA;
    18.     public Vector3 BB;
    19.  
    20.     // Start is called before the first frame update
    21.     void Start()
    22.     {
    23.      
    24.     }
    25.  
    26.     Vector3 GetMinimumEulerDistance(Vector3 last, Vector3 a, Vector3 b)
    27.     {
    28.         float aa = AngularDistance(last, a);
    29.         float bb = AngularDistance(last, b);
    30.         Debug.Log($"{a}:{aa} - {b}:{bb}");
    31.  
    32.         if (float.IsNaN(aa))
    33.             return MakePositive(b);
    34.         if (float.IsNaN(bb))
    35.             return MakePositive(a);
    36.  
    37.         if (aa.CompareTo(bb) < 0)
    38.         {
    39.             Debug.Log("Chose A");
    40.             return MakePositive(a);
    41.         }
    42.         else
    43.         {
    44.             Debug.Log("Chose B");
    45.             return MakePositive(b);
    46.         }
    47.     }
    48.  
    49.     float AngularDistance(Vector3 a, Vector3 b)
    50.     {
    51.         float cax, cay, caz, sax, say, saz, cbx, cby, cbz, sbx, sby, sbz;
    52.  
    53.         cax = Mathf.Cos(a.x * Mathf.Deg2Rad);
    54.         cay = Mathf.Cos(a.y * Mathf.Deg2Rad);
    55.         caz = Mathf.Cos(a.z * Mathf.Deg2Rad);
    56.         cbx = Mathf.Cos(b.x * Mathf.Deg2Rad);
    57.         cby = Mathf.Cos(b.y * Mathf.Deg2Rad);
    58.         cbz = Mathf.Cos(b.z * Mathf.Deg2Rad);
    59.         sax = Mathf.Sin(a.x * Mathf.Deg2Rad);
    60.         say = Mathf.Sin(a.y * Mathf.Deg2Rad);
    61.         saz = Mathf.Sin(a.z * Mathf.Deg2Rad);
    62.         sbx = Mathf.Sin(b.x * Mathf.Deg2Rad);
    63.         sby = Mathf.Sin(b.y * Mathf.Deg2Rad);
    64.         sbz = Mathf.Sin(b.z * Mathf.Deg2Rad);
    65.  
    66.         float ct1 = cbx - cax;
    67.         float ct2 = cby - cay;
    68.         float ct3 = cbz - caz;
    69.         float st1 = sbx - sax;
    70.         float st2 = sby - say;
    71.         float st3 = sbz - saz;
    72.  
    73.         return Mathf.Sqrt(ct1*ct1 + ct2*ct2 + ct3*ct3 + st1*st1 + st2*st2 + st3*st3);
    74.     }
    75.  
    76.     Vector3 MakePositive(Vector3 input)
    77.     {
    78.         return new Vector3((360f + input.x) % 360f, (360f + input.y) % 360f, (360f + input.z) % 360f);
    79.     }
    80.  
    81.     void CalculateEuler()
    82.     {
    83.         // I'm doing this the long way computationally. Feel free to make it more efficient to your needs.
    84.         Matrix4x4 m1 = Matrix4x4.TRS(Vector3.zero, Quat, Vector3.one);
    85.         M1 = m1;
    86.         UnityCurrentEulerChoice = Quat.eulerAngles;
    87.  
    88.         // We need to convert the matrix to YXZ (see https://www.andre-gaschler.com/rotationconverter/)
    89.         float cx, cy, cz, sx, sy, sz;
    90.         float cx2, cy2, cz2, sx2, sy2, sz2;
    91.         //cy*cz+sx*sy*sz,   -cy*sz+sx*sy*cz,    cx* sy
    92.         //cx*sz,            cx*cz,              -sx
    93.         //-sy*cz+sx*cy*sz,  sy*sz+sx*cy*cz,     cx*cy
    94.  
    95.         cx = Mathf.Sqrt(m1.m10 * m1.m10 + m1.m11 * m1.m11);
    96.         cx2 = -cx;
    97.         sx = -m1.m12;
    98.         sx2 = sx;
    99.         cy = m1.m22 / cx;
    100.         cy2 = m1.m22 / cx2;
    101.         sy = m1.m02 / cx;
    102.         sy2 = m1.m02 / cx2;
    103.         cz = m1.m11 / cx;
    104.         cz2 = m1.m11 / cx2;
    105.         sz = m1.m10 / cx;
    106.         sz2 = m1.m10 / cx2;
    107.  
    108.         Vector3 a = new Vector3(Mathf.Atan2(sx, cx) * Mathf.Rad2Deg, Mathf.Atan2(sy, cy) * Mathf.Rad2Deg, Mathf.Atan2(sz, cz) * Mathf.Rad2Deg);
    109.         Vector3 b = new Vector3(Mathf.Atan2(sx2, cx2) * Mathf.Rad2Deg, Mathf.Atan2(sy2, cy2) * Mathf.Rad2Deg, Mathf.Atan2(sz2, cz2) * Mathf.Rad2Deg);
    110.         AA = a;
    111.         BB = b;
    112.         try
    113.         {
    114.             Vector3 final = GetMinimumEulerDistance(LastEuler, a, b);
    115.             LastEuler = CurrentEuler;
    116.             CurrentEuler = final;
    117.         }
    118.         catch(System.Exception ex)
    119.         {
    120.             Debug.Log($"{ex}");
    121.         }
    122.     }
    123.  
    124.     // Update is called once per frame
    125.     void Update()
    126.     {
    127.         if (Input.GetKey(KeyCode.Space))
    128.         {
    129.             Vector3 inc = Axis * Amount;
    130.             if (Premultiply)
    131.             {
    132.                 Quat = Quaternion.Euler(inc) * Quat;
    133.                 Quat = Quat.normalized;
    134.             }
    135.             else
    136.             {
    137.                 Quat *= Quaternion.Euler(inc);
    138.                 Quat = Quat.normalized;
    139.             }
    140.             CalculateEuler();
    141.         }
    142.     }
    143. }
    144.  
    Quick Note about the Code:

    Pre-multiplying a Quaternion to a rotation rotates on the world XYZ, while post-multiplying a Quaternions rotates on the object's own XYZ, changing some of its own axes potentially for each rotation when post-multiplying.

    Combining pre and post multiplying operations you can achieve gimbal lock with Quaternions wherever the pitch angle is aligned to +/- 90 degrees the roll and yaw will move in the same direction in gimbal lock. If you set the increment amount to 1.0, and pre-multiply on the X axis you'll increment by 1, while post-multiplying on the Z axis increments on 1. This is essentially similar to adding and subtracting vectors, except for the Y axis, which I've found has a pre-multiply matrix that's similar to a XYX "proper" Euler rotation and a post-multiply matrix similar to a ZYZ with the exception of some sign flips.

    Most technical whitepapers I've read on the internet don't seem to cover this and unnecessarily hype Quaternions from a lack of understanding of how pre and post multiplying work, remember that Quaternion and Matrix multiplication is isomorphic.

    Unity3D hides all this math behind interop code that's not part of the C# reference source while some gaming companies still require you to know the math behind matrices and quaternions to work for them, so it's good to know that order of axes still matters in the creation of quaternions just as much as matrices, and that you can still get gimbal lock with both despite what people on the internet hype.

    I'm in the process of building a ThreeJS educational site with interactive quaternion and matrix solvers and calculators to help you tackle all aspects of rotations interactively.
     
  8. abdu2624

    abdu2624

    Joined:
    Aug 2, 2020
    Posts:
    1
    Here I'm trying to get the X axis

    it's quite simple, imagine 2 points on the circle one bellow the 0deg and one above it.
    Example:
    Point_Bellow = 1;
    Point_Above = 359;

    as you ca see if we want the Point_above to be as we wish we have to minus it from 360 which will make it
    Point_Above = 360 - 359 = 1;

    But you can't use the same for the Point_Bellow cuz it will just make it the same as before but just opposite
    So we have to check if the Camera.main.transform.localEulerAngles[0] which is the x angle is greater than 180
    if it is greater than 180 then the point is above the 0deg and we can use 360 - the angle otherwise you can write the angle directly or preceded by minus to make it negative

    Like this:

    if(Camera.main.transform.localEulerAngles[0] - 180 >= 1)
    {
    print(360 - Camera.main.transform.localEulerAngles[0]);
    }
    else
    {
    print(-Camera.main.transform.localEulerAngles[0]);
    }

    Another way to do it:
    Since the camera is rotated that means you are using the mouse or screen touch to rotate it, so you can from the beginning make a variable such as
    private float angel = 0;
    and change it every time you rotate the camera

    for example:
    Here is a code that check if the camera angle is inside a specific range and change it according to the mouse movement

    float y = Input.GetAxis("Mouse Y") * -1;
    if ((angel <= -45 & y > 0) || (angel >= 45 & y < 0) || (angel < 45 & angel > -45))
    {
    transform.Rotate(y, 0, 0);
    angel += y;
    }

    Hope anyone can use that, excuse my English xD as you can see I'm not a native speaker lol
     
  9. Cherubim79

    Cherubim79

    Joined:
    May 3, 2014
    Posts:
    56
    Except that localEulerAngles uses the first orientation from the Quaternion. A Quaternion or Matrix, which are isomorphic, return 2 possible orientations, unless at the poles, in which case is a singularity. Unity under the hood just uses the first one, which is ok for speed but not if you want the one closest to the original Euler angle, so at pole flips you get a different result. I’ve got code that fixes all this and gives you the closest result based on an angular distance utilizing cosine and sine and also by tracking the last Euler separately. I think I posted this already, but if not I’ll put it up on my GitHub.
     
  10. giantkilleroverunity3d

    giantkilleroverunity3d

    Joined:
    Feb 28, 2014
    Posts:
    383
    I know what lures people into this dilema:
    If I type in 28 to rotation y the object rotates to 28. Simple human effort.
    If I shove 28 into rotation.y with code, it works.
    If I read the rotations out and push back in then there is where the problems start.
    In my case I want to use the rotations of the camera as a starting point then rotate from there.
    Nay nay.
    The camera jumps to a zero or 0.0000111 type of size.
    I read the y, but it is not usable as the inspector value 28 but the numbers read in code are not.
    If I just give up and set everything to zero then all is well.
    I might as well look at a brick wall all day.

    My need to us capture the starting rotations and increment from there.
     
    Last edited: Nov 21, 2020
  11. giantkilleroverunity3d

    giantkilleroverunity3d

    Joined:
    Feb 28, 2014
    Posts:
    383
    I went and did some more testing and here is what I found:
    1: Do not expect to make sense of the inspector or the output of the debug.
    2:
    Vector3 eulerToVector3 = transform.rotation.eulerAngles;// No difference
    or
    Vector3 eulerToVector3 = transform.eulerAngles;// No difference
    or
    Vector3 eulerToVector3 = transform.localRotation.eulerAngles;// Yes Difference

    Debug.Log("<color=#008B8B>eulerToVector3.x: " + eulerToVector3.x + "</color>");
    Debug.Log("<color=#008B8B>eulerToVector3.y: " + eulerToVector3.y + "</color>");
    Debug.Log("<color=#008B8B>eulerToVector3.z: " + eulerToVector3.z + "</color>");
    eulerToVector3.y -= 10;
    transform.eulerAngles = eulerToVector3;
    3: This works.
    Now...
    When you need to copy the vector3 x and Y and place them into a vector2 rotation, Beware.
    The vector2 will have the values in the debug and when placed into the quaternion will cause y to not be correct.
    You have no access to the numbers 'seen' in the inspector gameobject rotation x,y,z.
     
    Last edited: Nov 22, 2020
  12. Cherubim79

    Cherubim79

    Joined:
    May 3, 2014
    Posts:
    56
    Feel free to check out my JavaScript 3D Rotation calculator with ThreeJS.

    https://johnnyernest.github.io/RotationCalculator.github.io/

    I've written code that uses angular distance along with storing the last rotation value to retrieve a readable result when pole changes occur on the pitch angle. Also a Quaternion Solver, and I show you the difference visually between pre and post Quaternion multiplication as well as Euler to Quaternion formulas.

    Did you know that you can get gimbal lock with Quaternion multiplication despite what lots of academic whitepapers say? It's true with pre and post Quaternion multiplication, and actually advantageous to use for utilizing different kinds of rotations, whether you have stationary objects rotating around one or two axes or you have free rotating objects.

    It would probably be fairly trivial to convert to an attachable script in Unity3D. I would have done this already, but I've been very busy with .NET Core and Xamarin lately.

    This is also a good 3D Rotation calculator in JS if you just need to convert between Euler and Quaternion / Matrix / Axis-Angle.

    https://www.andre-gaschler.com/rotationconverter/
     
    glenneroo likes this.
  13. blackfox_studio

    blackfox_studio

    Joined:
    Apr 8, 2019
    Posts:
    40
    This is perfect for solving rotation clamping problems. Thank you
     
  14. ubergeekseven

    ubergeekseven

    Joined:
    Oct 27, 2014
    Posts:
    5
    MY use case, for anyone needing a reason to need this, is for a projection mapping setup that was hung incorrectly and the build needs to be able to rotate and move objects to accurately align them to the wall objects. I understand that I can do that without this ability. I need it so that I can permanently code the locations and rotations for the build that will be used. My use is for setup only and the end users never need it after it is aligned. I could use a save system to hold all of the changed information but, I do not want to have to come do it if it ever corrupts.
    The end users are not capable of understanding anything in the end product behind the scenes. The whole thing is made to obfuscate the complexity while giving them the requested features in an easy to use interface.
    For all of the posts saying this is not needed and whatnot, there are some use cases for it and understanding how to achieve it alone is in itself a use case.
    It is odd that the people who do not need it are the only ones saying that it is not useful. While clearly others throughout the thread seem to find several solutions useful themselves. Thanks for all of the different explanations and math behind this. I absolutely would not have been able to achieve what I did without them. This thread was the only one with great options for the end goal.
     
  15. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,998
    Yes, a real use-case is when someone has abused rotation to hold non-rotation values. If the Inspector has 720 for the y-rotation scripts see that as 0 and it's 0 for every rotational purpose. No problems. But maybe the designer had y-rotation also include "spins". 730 could mean "has made 2 complete spins and is 10 degrees into the next". For that matter, an empty could be using rotation x for gold, y for silver and z for copper pieces.

    In the editor you'd need to yank out the raw #'s and put them in script variables. In that case, tinyAnt's
    UnityEditor.TransformUtils.GetInspectorRotation
    would actually be useful to fix those confused design decisions.
     
    Bunny83 likes this.
Thread Status:
Not open for further replies.