Transform.Rotate() stuck at 90 and 270 degrees

Has anyone had this problem? For some reason, when I use Transform.Rotate() around the x-axis (Vector3.right), the object that is rotating will invariably get stuck at 90 degrees and 270 degrees (Straight up and Straight down).

Anyone have any insight?
Thanks!

	if (leftStick.position.y >= buffer) {
		transform.Rotate(Vector3.right * leftStick.position.y * Time.deltaTime * rotationSpeed, Space.World);
		
	}	
	
	if (leftStick.position.y <= -buffer) {
		transform.Rotate(Vector3.right * leftStick.position.y * Time.deltaTime * rotationSpeed);	
	}

leftStick is an iPhone Joystick, that ranges from -1 to 1 depending on where the user moves the joystick. If the user holds the joystick up, the ship moves down as it should, and vice versa. When the ship’s x-rotation reaches 90 or 270 though, it no longer can rotate up or down. It gets stuck.

Do you need a rotation of 90 degrees or even >90? Or would a rotation limit of 85 degrees (resp.275 degrees) suffice?

Unity does some strange clamping/snapping for X rotations of/near 90/270 degrees, due to Gimbal lock problems. I think once the X-rotation exceeds 87 degrees, it will snap to some value, and trying to decrease that value again by small amounts (e.g., scaled by Time.deltaTime) will do nothing.

To prevent that from happening, you should limit your X rotation to, say, <=85 degrees and >=275 degrees.

I wrote this extension method to provide a “safe” x, based on the fact that rotations from -90 to 90 work fine with no Gimbal Lock. Thats exactly the area an FPS Player would be able to look (-90 is up, 90 is down). An x value of 120 e.g. would be translated to -60, which will work then. It could be extended to provide full 360°, by detecting the gimbal lock and add 180 to y then.

using UnityEngine;

public static class ExtensionVector3
{
	public static float CalcEulerSafeX(float x)
	{
		if (x >= -90 && x <= 90)
			return x;
		x = x % 180;
		if (x > 0)
			x -= 180;
		else
			x += 180;
		return x;
	}
	public static Vector3 EulerSafeX(this Vector3 eulerAngles)
	{
		eulerAngles.x = CalcEulerSafeX(eulerAngles.x);
		return eulerAngles;
	}
}

Usage in a simple Mouse script:

using UnityEngine;
using System.Collections;

public class PlayerMouse : MonoBehaviour {
	public float x = 0;
	public float y = 0;
	public float sens = 2.5f;

	void Start () {
		// either only call once to save initial rotation
		x = transform.localRotation.eulerAngles.EulerSafeX().x;
		y = transform.localRotation.eulerAngles.y;
	}

	void Update()
	{
		if (!Screen.lockCursor)
			return;

		x -= Input.GetAxis("Mouse Y") * sens;
		y += Input.GetAxis("Mouse X") * sens;
		x = Mathf.Clamp(x, -90, 90);
		
		// or handle publically changed x values always
		transform.localRotation = Quaternion.Euler(ExtensionVector3.CalcEulerSafeX(x), y, 0);
	}
}

Wow I’ve seen references to this issue dating back to '07. Same for me now using Unity 4… might be time for a bug fix:) Ok this was my fix to get it outside of the 3 degree dead stick range. You get a slight jump when passing that range but it’s better then limiting your functionality. Also Note that it locks at 90 and 270 when doing a transform.localEulerAngles.x = transform.localEulerAngles.x +1. I’m truly scratching my head why this bug still exists.

if (Input.GetKey(“s”)){
transform.Rotate(Vector3.right * Time.deltaTime * -50);
if (transform.localEulerAngles.x==270) transform.localEulerAngles.x = 267;
if (transform.localEulerAngles.x==90) transform.localEulerAngles.x = 87;
}

if (Input.GetKey(“w”)){
transform.Rotate(Vector3.left * Time.deltaTime * -50);
if (transform.localEulerAngles.x==270) transform.localEulerAngles.x = 273;
if (transform.localEulerAngles.x==90) transform.localEulerAngles.x = 93;
}