- Home /
Euler angle problems with rotation limit script
I have a script that limits the rotation of an object, which can be set in the inspector with any min/max value. This works as long as my rotation min/max values are positive, but breaks with negative values. So for example, limiting rotation between -45 and 45 degrees breaks. The problem I know has to do with euler angles, since they don't have negative values. A negative number x is converted to 360-x.
I suspect I can get around this using quaternions, but I don't really know how to do it. I need to read the X, Y and Z rotation values (the same values displayed in the Transform) and then apply the min/max limits.
Anybody understand quaternions well enough to point me in the right direction?
One other note... I really need to work with negative values because there may be cases where I want limits to be something like -360 to +360, which means the object should be able to rotate one full turn each direction and not any further. Euler angles would oversimplify this and allow the object to rotate continuously.
Hi Everyone and @selahattin-unlu and @Bunny83
Again I know this post is super old but I am having similar issues and can't seem to figure it out. I am still very new to program$$anonymous$$g and Unity.
This is my current code, as people have mentioned, localEulerAngles doesn't seem to take a negative number and I tried using some of the examples other uses have shown but still can't get it to work. Thank you!
void Update ()
{
float _mouseY = Input.GetAxis("$$anonymous$$ouse Y");
Vector3 newRotation = transform.localEulerAngles;
newRotation.x = $$anonymous$$athf.Clamp (newRotation.x - _mouseY, $$anonymous$$, max);
transform.localEulerAngles = newRotation;
}
@livefastdynasty Please don't post a question as an answer to an existing question. You should post your question in the help room.
Answer by Steven-Walker · Sep 22, 2011 at 01:42 AM
I'm going to answer my own question with the solution I found, but it's a bit less than ideal. I could find no way to get negative euler rotation values from quaternions. Somehow Unity is doing it in the Transform inspector, but I can't figure out how (maybe it's a UI trick). So instead, I'm offsetting my limit calculations so they always occur between 0 and 360. For example, with a rotation limit of -45 to 45, I'm offsetting the input values by 45 so the range calculated is 0 to 90, then offsetting it back -45. This solution works fine for rotation limits with a span less than 360 degrees.
Answer by selahattin-unlu · Dec 09, 2016 at 06:17 AM
Hi everyone, and @Steven-Walker I know this post is very old but someone may come here to solve their problem with euler angle.
I solved this issue by transforming all euler angles to quaternion. We can use negative values with quaternion. But if we use eulerAngle and if we set negative value on the editor, we'll see positive value in script. (As result: 360 - x.)
So, I assume we need to limit rotation between -50 and 50.
Quaternion rotationMin = Quaternion.Euler(new Vector3 (0f, 0f, -50f));
Quaternion rotationMax = Quaternion.Euler(new Vector3 (0f, 0f, 50f));
float moveHorizontal = Input.GetAxis ("Horizontal");
Quaternion rotation = transform.rotation;
if (moveHorizontal < 0 && rotation.z < rotationMax.z) {
rotation.z += Quaternion.Euler (new Vector3 (0f, 0f, 10f * Time.deltaTime)).z;
}
if (moveHorizontal > 0 && rotation.z > rotationMin.z) {
rotation.z -= Quaternion.Euler (new Vector3 (0f, 0f, 10f * Time.deltaTime)).z;
}
transform.rotation = rotation;
And I know this code may refactoring. But I just wanted to show logic. I hope it can help someones.
It seems that this code works for others, but it did not work for me.
But it gave me an idea and I made my own version similar to this one, and it worked!
Here's my version:
Quaternion rot$$anonymous$$in = Quaternion.Euler(new Vector3(0, 0, -30f));
Quaternion rot$$anonymous$$ax = Quaternion.Euler(new Vector3(0, 0, 30f));
Quaternion rotation = transform.rotation;
if(rotation.z <= rot$$anonymous$$in.z)
{
transform.eulerAngles = new Vector3(0, 0, -30f);
}
if(rotation.z >= rot$$anonymous$$ax.z)
{
transform.eulerAngles = new Vector3(0, 0, 30f);
}
I hope my code helps someone.
Answer by Bunny83 · Sep 22, 2011 at 01:42 AM
The rotations are always stored as Quaternion. Quaternions doesn't even work with angles in degree. A Quaternion defines a 3D-rotation as one thing and not like eulerAngles with 3 consecutive rotations. It describes the rotation by a 3D rotation-axis-vector and another float value that describes the rotation around that axis. But they work with normalized complex numbers and you can't work with them like you're used to (the euler-angles-representation)
This is the original inspector for the Transform component. As you can see the rotation that you see in the inspector is the Vector3 that is returned by localEulerAngles. However if the rotation is affected by the physics-system it can clamp the values or two of them jump by 180° when it would run into a gimbal-lock.
internal class TransformInspector : Editor
{
private bool firstSet = true;
private Vector3 rotation;
private Quaternion oldQuaternion;
public override void OnInspectorGUI()
{
EditorGUIUtility.LookLikeControls();
Transform transform = base.target as Transform;
EditorGUI.indentLevel = 0;
if (this.firstSet || this.oldQuaternion != transform.localRotation)
{
this.firstSet = false;
this.rotation = transform.localEulerAngles;
this.oldQuaternion = transform.localRotation;
}
Vector3 v = EditorGUILayout.Vector3Field("Position", transform.localPosition, new GUILayoutOption[0]);
this.rotation = EditorGUILayout.Vector3Field("Rotation", this.rotation, new GUILayoutOption[0]);
Vector3 v2 = EditorGUILayout.Vector3Field("Scale", transform.localScale, new GUILayoutOption[0]);
if (GUI.changed)
{
Undo.RegisterUndo(transform, "Transform Change");
this.rotation = this.FixIfNaN(this.rotation);
transform.localPosition = this.FixIfNaN(v);
transform.localEulerAngles = this.rotation;
this.oldQuaternion = transform.localRotation;
transform.localScale = this.FixIfNaN(v2);
}
EditorGUIUtility.LookLikeInspector();
}
private Vector3 FixIfNaN(Vector3 v)
{
if (float.IsNaN(v.x))
{
v.x = 0f;
}
if (float.IsNaN(v.y))
{
v.y = 0f;
}
if (float.IsNaN(v.z))
{
v.z = 0f;
}
return v;
}
}
}
There's no much you can do about that. Well, it depends on your case. If you have full control over the rotation you can use a Vector3 to store the 3 angles and use Quaternion.Euler to convert them into a rotation.
Thanks for this thorough reply! There's one thing I still don't understand about the Transform inspector. How does this line work? this.rotation = EditorGUILayout.Vector3Field("Rotation", this.rotation, new GUILayoutOption[0]);
It seems to me this is wrong... shouldn't it be localEulerAngles, since rotation is Quaternion?
And is it just a UI trick that negative numbers are displayed? If the UI is just storing the float values entered by the user, it could be converting them under the hood to euler angles while still preserving the appearance of negative values.
rotation inside the TransformInspector is defined as private Vector3 rotation;
at the top. When the object is selected it copies the localEulerAngles into the Vector3. However localEulerAngles will return negative values, but only when you set them manually to a negative value. All "automatic" conversions into eulerAngles will always be wrapped between 0-360. It seems Unity stores the given eulerangles internally inside the transform, but it doesn't serialize the values. If you set the x rotation to -360, save the scene and reload it or press play and stop again it will jump to 0.
So it's not really a trick by the UI. It's a trick by the Transform itself but only for the UI. The conversion from quaternion into eulerangles which happens at runtime will always return positive angles.
That makes sense. Thanks for the extra explanation.