- Home /
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.
I updated the OP. Thanks. Also, ignore that Space.World. That was me trying to see if that would make a difference, but it didn't.
I don't know if it can help, but I adapted this script to my PC Unity and it worked fine, rotating an object without any restriction. $$anonymous$$y object was simple capsule, not a CharacterController, and had no rigidbody.
have a look at @kungfooman answer, the extension wil calculate the new variable dependent on the value you want to set. Looks to me like the answer for this question.
Answer by Wolfram · May 30, 2011 at 03:44 AM
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, =275 degrees.
For a test, try setting rotation.EulerAngle.xyz to a public Vector3. You should see the snapping. A brute force fix is to declare a separate float Angle;
, have joysticks change that, and set directly to that angle each frame, using quaternion.Eulerangle.
Answer by kungfooman · Mar 03, 2015 at 12:51 PM
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);
}
}
This is exatly what I was looking for. This problem appears often, doesn't matter if you want to change the x parameter or another one. This is what I recognised, should be the answer to the question, but isn't there a possibility to use a method build in, to solve this problem?
Answer by ChrisR · Dec 10, 2012 at 07:50 AM
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; }
It's not a bug; Unity uses quaternions internally for rotations. Checking one element of eulerAngles is generally not a good idea since the conversion from quaternions to euler angles can be done in more than one valid way (e.g, 0,0,0 is the same as 180,180,180), and there's no guarantee that the conversion will turn out the same consistently.
There's a reason the docs tell you not to increment eulerAngles. The best way is to track rotation yourself like the standard $$anonymous$$ouseLook script does.
Your answer
Follow this Question
Related Questions
Rotate 90 over time on mouseDown 2 Answers
Calc 30° left and x units forward from local forward axis 1 Answer
Rotate player 360 degrees once 0 Answers