Search Unity

Acquiring rotation of gameobject issue

Discussion in 'Getting Started' started by oturaz, Sep 17, 2018.

  1. oturaz

    oturaz

    Joined:
    May 6, 2016
    Posts:
    13
    Hello there guys. I'm new here, try to be gentle XD
    So I have an object that I am only manipulating by applying forces to it in all 3 dimensions and I need to read the rotational values of the object (I need those rotations because I apply the forces based on the rotations). I tried a lot of different approaches for the past 3 days and while simply taking the rotational values with object.transform.localEulerAngles.x was the closest to what I need, the rotation around the x axis has a 180 degrees limit unlike the y and z axis (starting to decrease once it reaches 90 or 270). Unfortunately for me the rotational values around the x and z axis are some of the most vital part of my project and i need 360 degrees for both.
    Any ideas of how I could solve this issue?

    EDIT: here are some images of my problems and what I'm trying to do:




     
    Last edited: Sep 18, 2018
  2. Bill_Martini

    Bill_Martini

    Joined:
    Apr 19, 2016
    Posts:
    445
    That doesn't sound like normal behavior. Can you post your code? If your code is correct, you've stumbled on a huge bug as that's used quite a bit. That being said, I can't believe something like that would not be easily caught by QA.
     
  3. oturaz

    oturaz

    Joined:
    May 6, 2016
    Posts:
    13
    This isn't an issue of my code, as I use the game editor to turn my object around and check if the displayed values are correct. It isn't a bug either. This is how Unity is converting the quaternion into euler angles. I wouldn't have this issue if the x axis was switched with the y axis, since I don't mind if y axis values are displayed correctly or not. Unfortunately for me the formulas Unity is using for euler conversions favor y and z axis.
     
  4. Bill_Martini

    Bill_Martini

    Joined:
    Apr 19, 2016
    Posts:
    445
    Hmmm... Still having a problem thinking you can't get accurate rotation values on all three axis.

    Maybe copy the object's rotation into a new Vector3 and read those values??? If you are correct make you object a child of an empty object. Rotate the empty object so X is Y and place you're gameobject in the desired rotation. Then get you're rotations from the parent object.

    Just thought of something that may be causing the problem. When adding rotations there is a proper order to do it. Errors occur when not done in correct sequence. Off hand I don't know what that order is but check the manual it's in there somewhere.

    HTH
     
  5. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Please elaborate on this. You pretty much shouldn't use Euler angles for anything other than a rough debugging aid, ever. Anything you think you need Euler angles for, there is a much better way to do it (usually with quaternions).

    So? 190 degrees is the same as -170 degrees. The point is, there are many different ways to represent an actual rotation as Euler angles. You shouldn't put too much stock in what you see there.

    So, explain what it is you are actually trying to do, and we can probably suggest a way to do it. Whatever the answer is, it's not Euler angles. ;)
     
  6. oturaz

    oturaz

    Joined:
    May 6, 2016
    Posts:
    13
    I don't add rotation to my object. It is rotated purely by the addForce physics component of Unity.
    I tried attaching my object to an empty gameobject and rotate it so that x is y, but when i do that, the values of the euler angles unity gives me are no longer the way I need them (e.g. x gets changed when I rotate y). Without attching the empty gameobject and rotating, it behaves the way I need it, the only limitation is the 180 degrees on the x axis, which is vital for me.
     
  7. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    2,445
    If rotation-around-X is varying from -180 to 180 and you need to think of it in terms of 0 to 360, then you will just have to add 180 to the angle and change which way your model is facing.
     
  8. oturaz

    oturaz

    Joined:
    May 6, 2016
    Posts:
    13
    So I have a floating ship that I make move by applying forces to different parts of its body. But I want this ship to be somewhat parallel to the ground. When it rotates on the x axis, I want to add forces that compensate for that and push it back close to 0 degrees on that axis. For this, I need to read how much it has been turned around the local x axis to correct the pitch and the y axis to correct the yaw.
    Here are some images, maybe it'll make matters more clear:
    Here it doesn't have any rotation.

    Here it is rotated by 40 degrees around the x axis:

    Here it is rotated by 40 degrees around the z axis:

    And here it is rotated around the x axis by 34 degrees and around the z axis by 40 degrees.



    As I have previously mentioned I don't change any of the rotations myself and getting the rotations with transform.localEulerAngles is exactly what I need, but the rotation around the x axis is limited to 180 degrees unlike the y and z that go 360:


    If the y axis and x axis were switched, I wouldn't have this problem,and I tried attaching an empty gameobject to swap them, but then the values I get are no longer what I need (and I'm not talking about a shift by 90 of the values which would be child's play, the problem becomes that the values are no longer independent of each other).
    I tried calculating the euler angles from the quaternion myself, while prioritizing the z and x axis to have 360 degrees, but all the conversion ecuations I tried give me the global rotations, which are useless to me. The main issue I have is that rotating on one axis should not influence the values of the others; I want them to be independent.
    I thought that being able to procure the values displayed in the inspector would also help me solve this issue since they are independent of each other, just like I need, but from what I understand from other threads with people asking about them that is unfortunately impossible.

    I hope this explanation paints a better picture of what I'm trying to achieve and what are my limitations.
     
    Last edited: Sep 18, 2018
  9. oturaz

    oturaz

    Joined:
    May 6, 2016
    Posts:
    13
    It's not varying from -180 to 180. That would be way too easy. It goes from 0 to 90 and from 90 to 0. then from 360 to 270 and from 270 to 360. Basically it goes from -90 to 90, which is only half.
     
  10. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Aha! Now we can help you. :)

    Reading the Euler angles is (as pretty much always) not the right way to go about this. If you want to know how far your ship is away from vertical, you can use Vector3.Angle with Vector3.up and your ship's transform.up. But that'll give you the amount of tilt, not the direction of the tilt; to get separate pitch and roll, you'd need to zero out the local X and Z (of your local up vector) in turn.

    But all that is way over complicating it. There are easier ways to do this.

    If you were content to keep your ship exactly flat, you could simply lock its rotation (in the Rigidbody settings) in X and Z, allowing it to rotate only in Y. But I guess you want a "softer" response than that. You want something more like a drone or a hovercraft, that tilts a bit but then rights itself.

    So, one idea is to apply forces at the corners based on the Y position of each corner. You can easily find that with transform.TransformPoint (given the local position of each corner), and then compare the Y of that to the Y of the ship itself. Apply an upward or downward force in proportion to that distance. This should quickly level the ship out.
     
  11. oturaz

    oturaz

    Joined:
    May 6, 2016
    Posts:
    13
    Thank you for the answer! :D
    Unfortunately I also thought about applying the forces based on the height of the corners, but ultimately decided it's not what I need because it would be very difficult when I'm trying to reach other angles than parallel to the ground, for example while doing acrobatics or while upside-down. :(
    I used the parallel to the ground example because I thought it was the most simple way to explain it. Didn't expect it to cause misunderstandings. My bad here.
    Ideally what I want is to be able to specify the desired rotation on the x and z axis and the ship try its best (well, not really it's best, I want it to give the feeling of instability, if I wanted fixed values, I wouldn't use physics for this) to stay around those values.

    I was wondering if you could elaborate on the first approach you mentioned that you said is overcomplicating things. Perhaps it can help me. :D
     
    Last edited: Sep 18, 2018
  12. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Hmm. That does make it trickier (and I worry that the desired behavior while doing acrobatics or upside-down is not well-defined).

    OK, the idea I had before may be a little half-baked, but let's explore it. First break the problem into two: determine roll, and determine pitch.

    "Roll" means the angle between the local up and true up, but in the plane that's perpendicular to local forward. In other words, for the roll angle, we don't care about local Z (forward/back) — we only care about X and Y. So we could find this as follows:

    Code (CSharp):
    1. Vector3 up = transform.up;
    2. up.z = 0;  // zero out Z since we only care about X and Y for roll
    3. float roll = Vector3.Angle(up, Vector3.up);
    Then, for pitch, it's similar; we don't care about X in this case, but only care about Y and Z:

    Code (CSharp):
    1. up = transform.up;  // (get our local up vector again)
    2. up.x = 0;  // zero out X since we only care about Z and Y for pitch
    3. float pitch = Vector3.Angle(up, Vector3.up);
    Do these give you the angles you need? It works in my mind, but I haven't tried it on a computer. :)
     
  13. oturaz

    oturaz

    Joined:
    May 6, 2016
    Posts:
    13
    Unfortunately not. The rotation value of one axis is influenced by any of the other 2 rotations and the numbers I get are even more weird than when I try to convert the quaternion into euler engles. :(
     
  14. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Well, of course the three axes are intertwined; that's just how the universe works. But how are they "weird"? Please be specific. Do some simple controlled rotations, and post the numbers you get. I'm still thinking this works; one of us just needs to convince the other. :)
     
  15. oturaz

    oturaz

    Joined:
    May 6, 2016
    Posts:
    13
    Trust me, I wouldn't dare dismiss your suggestion before trying really hard to make it work, after all, this project is extremely important and quitting is not an option. But I do agree I should have given you a more detailed explanation of why it doesn't work.

    So here are the angles I get by using your suggestion. Euler_x is your pitch and euler_z is the roll. First I rotated the ship around the x axis by 33 degrees. Everything is fine as seen in the image:

    But after rotating it around the Y axis, both the pitch (x axis) and the roll (z axis) changed, which is quite an abnormal behaviour. And I think it is quite obvious from the image too that the roll should be 0 in this position:


    Now to make matters clear, I demonstrated the numbers I get for the same rotations using the values transform.localEulerAngles give me. You will notice that both euler_x and euler_z remain unchanged when I rotate the ship around the Y axis, which is what I expect to get and the behaviour I need:



    I appreciate your suggestion so so much though! I no longer have any new ideas of what to try and I'm becoming desperate. I'm starting to feel like there's no solution to my problem :( It's been 3 days of trying to solve this problem. I'm at my limit and I'm mentally exhausted, so any ideas you might have, please do not hesitate to throw them at me. Any lead at all will make me more than happy because I will have something new to try.
     
    Last edited: Sep 18, 2018
  16. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Ah, right. I collapsed the vectors wrong — once we get transform.up, it is in world space, and zeroing out the X or Z component is only right if the object happens to be facing world-forward.

    What we actually want to do is project the local up and the world up onto a plane that depends on which way the object is facing. For roll, we want to project it onto a plane matching the front of the vehicle. And for pitch, we'd want to project on a plane matching the side of the vehicle. So perhaps:

    Code (CSharp):
    1.     Vector3 localUp = Vector3.ProjectOnPlane(transform.up, transform.forward);
    2.    Vector3 worldUp = Vector3.ProjectOnPlane(Vector3.up, transform.forward);
    3.     float roll = Vector3.Angle(localUp, worldUp);
    4.  
    5.     localUp = Vector3.ProjectOnPlane(transform.up, transform.right);
    6.     worldUp = Vector3.ProjectOnPlane(Vector3.up, transform.right);
    7.     float pitch = Vector3.Angle(localUp, worldUp);
     
    oturaz likes this.
  17. oturaz

    oturaz

    Joined:
    May 6, 2016
    Posts:
    13
    Thanks, this solution really sounds like it should work! :D
    I've only done a quick test because I have to leave home soon. I'll do some more extensive testings this afternoon, but at first glance the angles I get seem to be pretty good. The only issue now is that Vector3.Angle gives the shortest distance between angles and after a rotation of 180 degrees, the value starts going down, meaning it can't make the difference between up and down. Similar to the issue with transform.localEulerAngles, that goes from -90 to 90. Any idea how this can be fixed? I'll also think of something until I get back home, but I'm not proficient in vector manipulation so I might be unable to find a solution.
     
  18. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Perhaps this will help with that:
    Code (CSharp):
    1.         /// <summary>Get a signed angle between two vectors</summary>
    2.         /// <param name="from">Start direction</param>
    3.         /// <param name="to">End direction</param>
    4.         /// <param name="refNormal">This is needed in order to determine the sign.
    5.         /// For example, if from an to lie on the XZ plane, then this would be the
    6.         /// Y unit vector, or indeed any vector which, when dotted with Y unit vector,
    7.         /// would give a positive result.</param>
    8.         /// <returns>The signed angle between the vectors</returns>
    9.         public static float SignedAngle(Vector3 from, Vector3 to, Vector3 refNormal)
    10.         {
    11.             float angle = Vector3.Angle(from, to);
    12.             if (Vector3.Dot(Vector3.Cross(from, to), refNormal) < 0) return -angle;
    13.             return angle;
    14.         }
    15.  
     
    oturaz and Bill_Martini like this.
  19. oturaz

    oturaz

    Joined:
    May 6, 2016
    Posts:
    13
    Oh, wow. I didn't expect it to be this simple. Many thanks! But I'm not quite sure I understand what the refNormal would be in my example. The transform.forward and transform. right perhaps?
     
  20. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Yes. The reference normal is a vector roughly perpendicular to the angle you're trying to measure.
     
    oturaz and Bill_Martini like this.
  21. oturaz

    oturaz

    Joined:
    May 6, 2016
    Posts:
    13
    This works wonderfully! Thank you so much for all your help, I have no idea what I would have done without your help!
     
    Last edited: Sep 20, 2018
    JoeStrout likes this.