# Rotating on X axis causing Y and Z to change and swap...

Discussion in 'Getting Started' started by Tset_Tsyung, Jan 23, 2016.

1. ### Tset_Tsyung

Joined:
Jan 12, 2016
Posts:
364
(EDIT: Please note, this is now solved - anyone reading looking for the solution should see the 9th post for my work around... may not be best solution, but it worked fine for me)

Hey all,

This has been bugging me all day, and so I've stripped back all of the code and gotten to this in the FixedUpdate
(FYI, starting rotations on X, Y and Z are 270, 180 and 0 respectively)

Code (CSharp):
1.     void FixedUpdate()
2.     {
3.         float newXrot;
4.
5.         newXrot = m_inputXValue * m_tiltSpeed * Time.deltaTime;
6.
7.         if (transform.eulerAngles.x > 290f && m_inputXValue < 0)
8.             newXrot = 0f;
9.
10.         transform.Rotate (newXrot, 0f, 0f, Space.Self);
11.     }
The if statement works fine, it stops rotating past 290 degrees (this has been hard coded for testing purposes, originally was a variable). But it's what's happening to the other angles that are interesting.

Rotate positively on the X and, even though the object looks EXACTLY the same in the game view, the Z rotation will now say 180deg and the Y rotations will show -1.8669e. Rotate the other way BELOW 270 and Y becomes 180 and the Z shows -9.2....e and keeps changing. Yet, and I cannot stress this enough, the object shows NO change on the Y and Z axis.

This is, of course causing me serious headaches because as I'm trying rotate ALSO on the Z axis (player controls board, allowing ball to navigate) it keeps throwing me off, and whatever code I've been trying to write for the last 8 hours throws 'fits' when I try to rotate on the X below 270...

P.S. Have tested further, when rotating below 270 on the X axis, is visually rotated fine... but the X axis value starts climbing, causing the if statement to kick and not allow me to return to the start rotation...

Last edited: Jan 24, 2016
2. ### Tset_Tsyung

Joined:
Jan 12, 2016
Posts:
364
A thought has occurred to me...

The board is a mesh that's imported from blender (teaching myself this as well...) and when it imports I have to set it to it's "Starting position's" of 270 and 180 (x, y) would this cause the rotations problems later on? I wouldn't have thought so, but I can't think of what else could be doing this...

3. ### JoeStrout

Joined:
Jan 14, 2011
Posts:
8,192
The thing is, you are trying to merely update the transform based only on values stored in that transform. And not only that, you're trying to make use of the EulerAngle values!

The not-painful way of doing it is: define your own properties for however you want to keep track of your rotations. Maybe it's yaw (rotation around Y) and pitch (rotation around X), for example. Your input-handling code or AI or whatever only updates these; it does not touch the transform at all. Then, you recompute the rotation of the transform from these properties, completely ignoring whatever the transform contained before, e.g.:

Code (CSharp):
1. transform.rotation = Quaternion.Euler(yaw, pitch, 0);
This will work, 100% reliably, every time, no possibility of gimbal lock or other weirdness.

Please give it a try, be delighted and amazed, and feel free to thank my by writing a positive review of PixelSurface.

This will work reliably, and will

4. ### Tset_Tsyung

Joined:
Jan 12, 2016
Posts:
364
[Kisses @JoeStrout 's hand]

You, Sir, are a gentleman and a scholar!!! I tried it, I was delighted and Amazed... and have no idea what PixelSurface is... Seriously, what is it?!?!?!

For anyone having the same problem I now have this code in place:

Code (CSharp):
1.     void FixedUpdate()
2.     {
3.         float newXrot;
4.         float newZrot;
5.
6.         newXrot = m_inputXValue * m_tiltSpeed * Time.deltaTime;
7.         newZrot = m_inputZValue * m_tiltSpeed * Time.deltaTime;
8.
9.
10.
11.         transform.Rotate (newXrot, newZrot, 0f, Space.Self);
12.     }
This is still not finished, FYI...

Wait, am I to take it that Transform.eulers isn't ALWAYS reliable?!?!?!

5. ### JoeStrout

Joined:
Jan 14, 2011
Posts:
8,192
I'm glad it worked for you! I was mostly kidding about PixelSurface, but if you're curious, there is a link in my signature.

Er, but your code is still rotating the transform directly. That's not what I was recommending. Something more like this:

Code (CSharp):
1. using UnityEngine;
2.
3. public class Rotator : MonoBehaviour {
4.     float yaw;        // rotation around Y
5.     float pitch;    // rotation around X
6.
7.     public void Update() {
8.         // ...stuff here to get m_inputXValue and m_inputYValue...
9.         // then:
10.
11.         // Update our properties based on the inputs
12.         yaw += m_inputXValue * m_tiltSpeed * Time.deltaTime;
13.         pitch += m_inputYValue * m_tiltSpeed * Time.deltaTime;
14.
15.         // SET (not update!) our transform from our properties
16.         transform.localRotation = Quaternion.Euler(yaw, pitch, 0);
17.     }
18. }
See the difference? It's important that your yaw and pitch be properties, not local variables. You're going to update these based on the inputs. As you hold the right input, the yaw value is going to continuously increase. As it should, since it represents how far your object should be rotated around Y. Not the change in rotation! The actual rotation value.

Then, when you update your transform, you don't call transform.Rotate. As a rule of thumb: never call transform.Rotate. It does not set the rotation; it applies an additional rotation to the current rotation. That's not what you want, since you now know (from your pitch and yaw properties) exactly what the rotation should be. So you assign a new rotation. There are several ways to do that, but I usually just assign to transform.localRotation.

Yes, effectively so. It's not that there's anything wrong with how Unity implemented them; it's that Euler angles are an inherently flawed way of representing a rotation. They're darned convenient for some things, but if you try to read them, do some computations with them, and assign them back, you will always end up in trouble.

6. ### Tset_Tsyung

Joined:
Jan 12, 2016
Posts:
364
Ah, yes I see.

I wondered why I had these m_currentX/ZRotation floats that I wasn't using... lol. Obviously, because I was using transform.Rotate I didn't need them.

I understand a bit more now... but think I'm still missing a lot - I'll have to look up Unity tips and best practice guides to understand the difference between the different rotate functions...

Again, thank you so much I really appreciate you kind assistance.

EDIT: Wait! Yes I do get the difference, transform.rotation ans transform.localrotation sets the ACTUAL rotation of the object completely, whereas transform.rotate merely rotates a little bit more between frames (hence why you said one SETS and one UPDATES). Get what you're saying now. Thanks again bud! XD

Last edited: Jan 24, 2016
JoeStrout likes this.
7. ### Tset_Tsyung

Joined:
Jan 12, 2016
Posts:
364
... and now have Gimbal weirdness... [sigh]... I'm such a muppet, lol.

I need coffee...

8. ### Tset_Tsyung

Joined:
Jan 12, 2016
Posts:
364
Okay, I now have a head and face shaped hole in my brick wall... and still not getting anywhere.

I'm changed the code so that I'm now ASSIGNING (and not updating) my rotations each frame. However I now have funky gimbal lock both in code and the editor.

If I go back to my original version of the script below, it behave (at least in code) as it should:

Code (CSharp):
1. using UnityEngine;
2. using System.Collections;
3.
4. public class BoardMover : MonoBehaviour {
5.
6.     public float m_maximumTilt = 45f;
7.     public float m_tiltSpeed = 5f;
8.
9.     private float m_initialXRot = 270f;
10.     private float m_initialZRot = 0f;
11.     private float m_currentXRot;
12.     private float m_currentZRot;
13.     private float m_inputXValue;
14.     private float m_inputZValue;
15.     private bool m_resetButton;
16.
17.
18.     // Use this for initialization
19.     void Start ()
20.     {
21.         m_resetButton = false;
22.
23.         //get the Initial rotation of the board and store it for reference and for setting up the current rotations
24.         m_currentXRot = m_initialXRot = transform.rotation.eulerAngles.x;
25.         m_currentZRot = m_initialZRot = transform.rotation.eulerAngles.z;
26.
27.         Debug.Log ("X and Z rotations are " + m_currentXRot + " " + m_currentZRot);
28.
29.     }
30.
31.     // Update is called once per frame
32.     void Update ()
33.     {
34.         m_inputXValue = Input.GetAxisRaw ("Vertical");
35.         m_inputZValue = Input.GetAxisRaw ("Horizontal");
36.
37.         m_resetButton = Input.GetButton ("Jump");
38.
39.         /*
40.         m_currentXRot += m_inputXValue * m_tiltSpeed * Time.deltaTime;
41.         m_currentZRot += m_inputZValue * m_tiltSpeed * Time.deltaTime;
42.
43.         transform.localRotation = Quaternion.Euler (m_currentXRot, 180f, m_currentZRot);
44.         Debug.Log (transform.rotation);
45.         */
46.
47.     }
48.
49.     void FixedUpdate()
50.     {
51.         float newXrot;
52.         float newZrot;
53.
54.         newXrot = m_inputXValue * m_tiltSpeed * Time.deltaTime;
55.         newZrot = m_inputZValue * m_tiltSpeed * Time.deltaTime;
56.
57.         transform.Rotate (newXrot, newZrot, 0f, Space.Self);
58.     }
59.
60. }
61.
However, when I adjust to the newer version of the script (remove fixedupate code and the block code markers) where I'm assigning the rotations it locks Y and Z together (because initial x rotation is 270, they align).

Can I do either of the following?

1) Import my mesh and assign my axes manually? Or do I have to rotate in Blender and reimport?

2) Adjust the axes in code so that I can rotate the object according to how I'm looking at it?

3) Why does transform.rotate function as I would like it to when transform.rotation does not - aren't they supposed to adjust the same properties (albeit in different ways).

The thing I can't quite understand (and watching a blender animation video helped, of all things) is that you can ALIGN the axes during rotation... but shouldn't the axes by rotating AS A WHOLE and therefore stay in their relative positions during the rotation process, no matter what order you rotate them in?

P.S. @JoeStrout - I still appreciate the help you gave, just trying to nail home the understanding... Feel free to walk away, throwing your hands in the air at any point... lol

9. ### Tset_Tsyung

Joined:
Jan 12, 2016
Posts:
364
SOLVED!

Whilst hugging my emotional fragile wife (long story...) a solution hit me like a wet sponge fired from some kind of gas powered sponge gun...

If setting an intial rotation cause a problem with trying to rotate later (due to axes aligning with one another during rotations - still not sure WHY this happens, more research needed) then set it's rotation to 0, 0, 0 and rotate either the world, camera and gravity around it (stupid idea!) or attach it to a parent object and set THE PARENT'S rotation to what you want. This allows you to manipulate the child object's rotations from 0, 0, 0 whilst still having it set the way you want.

Like I said, this worked for me. There may be more elegant and professional solutions (and in future projects where I have to rotate an object many times over past the small amount needed here this will probably be necessary), but for now I can carry on with my project... until I get stuck at my next hurdle.

Happy coding all!

XD

rafael_galindo likes this.
10. ### JoeStrout

Joined:
Jan 14, 2011
Posts:
8,192
The technique in the commented-out code is not susceptible to gimbal lock. It is susceptible to not being the rotations you want, which is what I suspect was going on. That could be fixed by just changing where/how you apply them. (For example, I notice in the .Rotate code, you're applying newZrot to Y rather than Z.)

BUT, it's clear that working with the object in its sideways import orientation is confusing! Sticking it inside another GameObject, and applying your scripts to that, is a perfectly valid solution in such a case.

(I would still urge you to switch to the assigning-rotation-from-properties technique to avoid more grief later, though.)

Tset_Tsyung likes this.
11. ### Tset_Tsyung

Joined:
Jan 12, 2016
Posts:
364
Don't worry kind sir, the final script has this at the end... I believe this is what you meant...

Code (CSharp):
1.         m_currentXRot += m_inputXValue * m_tiltSpeed * Time.deltaTime;
2.         m_currentZRot += m_inputZValue * m_tiltSpeed * Time.deltaTime;
3.
4.         transform.localRotation = Quaternion.Euler (m_currentXRot, 0f, m_currentZRot);
I also included a few ifs and reset buttons and it all works GREAT! thanks again bud! XD

maliceCO and JoeStrout like this.

Joined:
Jan 14, 2011
Posts:
8,192

13. ### rafael_galindo

Joined:
Jun 30, 2016
Posts:
5
Thanks! I spent 2 weeks looking to move an object on its Z axis but every time it moves around another object, the z axis changes!!! but I did the parent trick and now I can move it always on the Z axis!.

I still don't know why the axis change when rotating an object

Cheers