# Local Rotation/Quaternion Problem

Discussion in 'Scripting' started by Predster, Jun 19, 2008.

1. ### Predster

Joined:
Sep 11, 2007
Posts:
145
I've modified the mouselook script to also allow for rotation on the z-axis. My big problem is that if you rotate yourself upside down, your x and y mouselook directions are reversed (it is even more problematic if you rotate 90 degrees because your horizontal and vertical are reversed).

If you have an upward input from mouseY (no matter what your z-rotation is), it always makes the camera look in the global upward direction, not the upward direction the camera is at. It seems as though the axis of rotation is not local. I can't seem to understand quaternions and just rotation in general, and I was wondering if anyone could help me out!

Code (csharp):
1. using UnityEngine;
2. using System.Collections;
3.
4.
6. public class MouseLookZ : MonoBehaviour {
7.
8.     public enum RotationAxes { MouseXAndYAndZ = 0, MouseX = 1, MouseY = 2 }
9.     public RotationAxes axes = RotationAxes.MouseXAndYAndZ;
10.     public float sensitivityX = 15F;
11.     public float sensitivityY = 15F;
12.     public float sensitivityZ = 15F;
13.
14.     public float minimumX = -360F;
15.     public float maximumX = 360F;
16.
17.     public float minimumY = -60F;
18.     public float maximumY = 60F;
19.
20.     public float minimumZ = -360F;
21.     public float maximumZ = 360F;
22.
23.
24.     float rotationX = 0F;
25.     float rotationY = 0F;
26.     float rotationZ = 0F;
27.
28.
29.     Quaternion originalRotation;
30.
31.     void Update ()
32.     {
33.         if (axes == RotationAxes.MouseXAndYAndZ)
34.         {
35.             // Read the mouse input axis
36.             rotationX += Input.GetAxis("Mouse X") * sensitivityX;
37.             rotationY += Input.GetAxis("Mouse Y") * sensitivityY;
38.             rotationZ += Input.GetAxis("Mouse Z") * sensitivityZ;
39.
40.             rotationX = ClampAngle (rotationX, minimumX, maximumX);
41.             rotationY = ClampAngle (rotationY, minimumY, maximumY);
42.             rotationZ = ClampAngle (rotationZ, minimumZ, maximumZ);
43.
44.             Quaternion xQuaternion = Quaternion.AxisAngle (Vector3.up, Mathf.Deg2Rad * rotationX);
45.             Quaternion yQuaternion = Quaternion.AxisAngle (Vector3.left, Mathf.Deg2Rad * rotationY);
46.             Quaternion zQuaternion = Quaternion.AxisAngle (Vector3.forward, Mathf.Deg2Rad * rotationZ);
47.
48.             transform.localRotation = originalRotation * xQuaternion * yQuaternion * zQuaternion;
49.
50.
51.         }
52.             }
53.
54.     void Start ()
55.     {
56.         // Make the rigid body not change rotation
57.         if (rigidbody)
58.             rigidbody.freezeRotation = true;
59.         originalRotation = transform.localRotation;
60.     }
61.
62.     public static float ClampAngle (float angle, float min, float max)
63.     {
64.         if (angle < -360F)
65.             angle += 360F;
66.         if (angle > 360F)
67.             angle -= 360F;
68.         return Mathf.Clamp (angle, min, max);
69.     }
70. }

2. ### spymaster

Joined:
Jun 3, 2009
Posts:
30
Hi.

Quaternions store rotations as a four dimensional vector. The values aren't that meaningful to you when it comes to modifying them. You should use the Transform methods to apply rotations to an object. You can read the angular values of the axes in degrees by reading the euler angles. It's a good idea to look at the code for the mouselook script etc or maybe the star trooper game.

I use this code below after setting up a Vector3 named euler that is the result of the previous rotation value (that I keep track of as a class field) being incremenally changed.

Note also my axes are a bit wacky because I'm grabbing value from the accelermometer with the phone on landscape orientation. I had tilt or lean working fine.

euler.y += currentAcceleration.x * turnSpeed;
euler.z = Mathf.Lerp(euler.z, -currentAcceleration.z * maxTilt, 0.2f);
euler.x = Mathf.Lerp(euler.x, currentAcceleration.y * maxTilt, 1.0f);

Quaternion rot = Quaternion.Euler(euler);
transform.rotation = Quaternion.Lerp (transform.rotation, rot, sensitivity);

I'm a newbie too. Good luck!

3. ### cyb3rmaniak

Joined:
Dec 10, 2007
Posts:
162
Sorry if I seem a bit fuzzy, I'm pretty hung over right now...

First of all - I'm dying to know - what exactly is Input.GetAxis("Mouse Z")??? What did you tie this to? the scroll wheel?

The mouselook script, although it modifies transform.localRotation, does NOT calculate rotations locally...

The problem is these lines:
Code (csharp):
1.
2. Quaternion xQuaternion = Quaternion.AxisAngle (Vector3.up, Mathf.Deg2Rad * rotationX);
3. Quaternion yQuaternion = Quaternion.AxisAngle (Vector3.left, Mathf.Deg2Rad * rotationY);
4. Quaternion zQuaternion = Quaternion.AxisAngle (Vector3.forward, Mathf.Deg2Rad * rotationZ);
5.
The AxisAngle function (which is AngleAxis in the docs BTW) creates a rotation around an axis - in this case - 3 global ones. Vector3.up is the global up vector (0,1,0), same for Vector3.left (-1,0,0) and Vector3.forward (0,0,1). So all rotations are basically global...

What you need is to create rotations around the relative axes of your camera. And this can be done using Transform.TransformDirection:

Code (csharp):
1.
2. Vector3 v3RelativeUp = transform.TransformDirection (Vector3.up);
3. Vector3 v3RelativeLeft = transform.TransformDirection (Vector3.left);
4. Vector3 v3RelativeForward = transform.TransformDirection (Vector3.forward);
5.
6. Quaternion xQuaternion = Quaternion.AxisAngle (v3RelativeUp, Mathf.Deg2Rad * rotationX);
7. Quaternion yQuaternion = Quaternion.AxisAngle (v3RelativeLeft, Mathf.Deg2Rad * rotationY);
8. Quaternion zQuaternion = Quaternion.AxisAngle (v3RelativeForward, Mathf.Deg2Rad * rotationZ);
9.
Again... I'm pretty tired and wasted, so this is untested, and some more tweaking would be necessary, but this should get you stated. unityunity