[OAFAT]Why does transform.rotation.x = 45 not work

This is a reference answer for questions that have code that attempt to directly manipulate a rotation’s x, y, or z component.

Summary:

  • The x, y, and z components of transform.rotation are not angles and cannot be manipulated as angles.
  • You can use transfrom.eulerAngles, but there are some serious issues you need to understand to avoid problems.
  • Transform.Rotate() is often a simpler and better solution for rotating than trying to change the angles directly.

Quaternions are not angles

Transform.rotations is a Quaternion…a non-intuitive, 4D construct based on complex numbers. The individual x, y, z, and w components are not angles and have values between -1.0 an 1.0. Unless you have a firm understanding of Quaternion math, I recommend against directly accessing x,y,z, and w. From the reference:

[Quaternions] are based on complex numbers and are not easy to understand
intuitively. You almost never access
or modify individual Quaternion
components (x,y,z,w).

In fact, of the hundreds of rotation questions I’ve answered, and the thousands I’ve read, I’ve never seen a question that required directly accessing x,y,z or w to solve. Here are some references to understand more about Quaternions in general and the use of Quaternions in Unity3D:

  1. UnityGem’s excellent tutorial: unitygems.com - unitygems Resources and Information.

  2. Wikipedia article: http://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation

  3. Official Unity Video on Quaternions: https://www.youtube.com/watch?v=TdjoQB43EsQ

  4. Understanding Quaternions: Understanding Quaternions | 3D Game Engine Programming

Quaternions are not required for many rotations

Many rotations (and likely the one you were attempting) can be accomplished through other (simpler) methods, including ‘Transform.Roate()’ and directly accessing ‘Transform.eulerAngles’:

Transform.Rotate() - Rotate() does a relative rotation, and by default, a local rotation. For example:

 transform.Rotate(0,45,0);

…will rotate and additional 45 degrees from the current rotation on the object’s ‘y’ axis. There is an optional second parameter, so:

 transform.Rotate(0,45,0,Space.World);

…rotates the object 45 degrees on the world ‘y’ axis. Here is a bit of sample code. Use the arrow keys to rotate. Use the shift key to change from local to world rotation:

#pragma strict

var speed = 90.0;  // Degrees per second;

function Update() {
    var x = Input.GetAxis("Vertical") * speed * Time.deltaTime;
    var y = -Input.GetAxis("Horizontal") * speed * Time.deltaTime;
    
    if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) {
            transform.Rotate(x,y,0,Space.World); // World rotation;
    }
    else {
            transform.Rotate(x,y,0);  // Local rotation
    }
}

EulerAngles - Within limits you can access angles like you are probably trying to do with ‘transform.rotation’. These three all do the same thing:

 transform.eulerAngles = someVector3;
 transform.rotation.eulerAngles = someVector3;
 transform.rotation = Quaternion.Euler(someVector3);

Before you get too excited, you need to understand the limits of eulerAngles:

  1. They suffer from gimble lock. It is rare for beginners to get in a situation where gimble lock is a problem, but it does happen. Some references on gimble lock:
  1. EulerAngles are derived from the Quaternion and do not maintain a static representation. For example, execute this code:

    transform.eulerAngles = (180,0,0);
    Debug.Log(transform.eulerAngles);

The output will be (0,180,180)…the same physical rotation represented differently. This changing representation makes eulerAngles difficult for may things, like clamping a rotation or following a rotation. In addition it leads to the next problem:

  1. EulerAngles must be assigned all at once. You cannot assign x,y and z independently. This is only a problem for Javascript/Unityscript since the C# compiler will not allow you to assign them independently.

  2. For many rotation situations, when using eulerAngles you have to deal with the 360/0 boundary.


One way to work around the changing representation problem is to treat EulerAngles as write-only…to never read back the representation. Here is an example:

#pragma strict

public var speed : float = 90.0;  // Degrees per second;
public var euler : Vector3 = Vector3.zero;
public var xLimit = 80.0;
public var yLimit = 80.0;

function Start() {
    transform.eulerAngles = euler;
}

function Update() {
    euler.x += Input.GetAxis("Vertical") * speed * Time.deltaTime;
    euler.y -= Input.GetAxis("Horizontal") * speed * Time.deltaTime;
    euler.x = Mathf.Clamp(euler.x, -xLimit, xLimit);
    euler.y = Mathf.Clamp(euler.y, -yLimit, yLimit);
    
    transform.eulerAngles = euler;
}

This code does a world rotation of a object with a clamp on the angles of rotation. All changes are made to the ‘euler’ variable, and then the variable is assigned. Since I never read from ‘transform.eulerAngles’, I don’t have to worry about changing representation. Since I control the representation, I can do anything to it I want.

This will simply work for you:

gameObject.transform.Rotate(new Vector3(0, 0, -1));

void Update()
{
if (Input.GetMouseButton(0))
{
transform.eulerAngles += new Vector3(speed, 0, 0);
}
if (Input.GetMouseButton(1))
{
transform.eulerAngles -= new Vector3(speed, 0, 0);
}

I try this on void update, and I find out that if you click the left mouse, it rotate and stop at the top that @robertbu mentioned. But then you stop the left mouse and immediately click the right mouse, it rotate to the bottom and stop again. What happend?
see this in youtube: - YouTube