Rotation with respect to world instead of the object itself using Quaternion.Euler in unity

[Other answers on similar questions on stackexchange didnt help]

This script is giving some weird outputs

On running this script If I press rightarrow once followed by 's' the object rotates on 'z' axis and 'x' axis correctly but there after if I press arrowkeys or 'a/s' in both the cases the object rotates on y axis forever. I dont understand why this is happening.

I want it to rotate w.r.t. the world axis always.

code:

public float speed = 1;
private int xtimesHit = 0;
private int ytimesHit = 0;
private int ztimesHit = 0;

void Update () {
if( Input.GetKeyDown( KeyCode.RightArrow ) ){
ztimesHit--;
}
if( Input.GetKeyDown( KeyCode.LeftArrow ) ){
ztimesHit++;
}

if( Input.GetKeyDown( "a" ) ){
ytimesHit++;
}
if( Input.GetKeyDown( "d" ) ){
ytimesHit--;
}

if( Input.GetKeyDown( "w" ) ){
xtimesHit++;
}
if( Input.GetKeyDown( "s") ){
xtimesHit--;
}

this.transform.rotation = Quaternion.Lerp(this.transform.rotation, Quaternion.Euler(xtimesHit*90,ytimesHit*90,ztimesHit*90), Time.deltaTime*speed);


How can I do that? I have created this code with help from other, so I'd wish to edit it rather than using totally new functions.

[Optional] Code explanation: My code is fulfilling partial requirements, I want to rotate my object on x,y,z axis by 90 deg on respective keyboard inputs. (Example: keys a and s should rotate the object by 90 deg on z axis, w and s should rotate the object by 90 deg on y axis and arrow keys should rotate the object by 90 deg on y axis). Also pressing 'a' n times should rotate the object by 90 deg n times.

• This is a phenomenon called Gimbal Lock, and it occurs because you're specifying your rotation in Euler angles (Quaternion.Euler). In general, the result of doing X rotations about the world x-axis, Y rotations about world y, and Z rotations about world z, depends on the order of those rotations, so simply counting occurrences isn't enough. You'll need a substantial refactor. – DMGregory Jun 1 '15 at 14:42
• Substantial refactor? like what? How can I solve this problem? – nameetemp Jun 1 '15 at 15:43
• Patience, grasshopper. You'll find an answer below. ;) – DMGregory Jun 1 '15 at 15:44

1 Answer

Because rotations are order-dependent, as described in my comment above, the internal state you store needs to fully-describe an orientation.

In general, you can do this by storing a targetOrientation quaternion. If you're only ever doing rotations of +-90 degrees about the x/y/z axes then you can store a more reduced state (there are only 24 possible values), but I think this version is clearer to read:

public float speed = 1;
private Quaternion targetOrientation = Quaternion.identity;

void Update () {
if( Input.GetKeyDown( KeyCode.RightArrow ) ){
targetOrientation = Quaternion.Euler(0, 0, -90) * targetOrientation;
}
if( Input.GetKeyDown( KeyCode.LeftArrow ) ){
targetOrientation = Quaternion.Euler(0, 0, 90) * targetOrientation;
}

if( Input.GetKeyDown( "a" ) ){
targetOrientation = Quaternion.Euler(0, 90, 0) * targetOrientation;
}
if( Input.GetKeyDown( "d" ) ){
targetOrientation = Quaternion.Euler(0, -90, 0) * targetOrientation;
}

if( Input.GetKeyDown( "w" ) ){
targetOrientation = Quaternion.Euler(90, 0, 0) * targetOrientation;
}
if( Input.GetKeyDown( "s") ){
targetOrientation = Quaternion.Euler(-90, 0, 0) * targetOrientation;
}

this.transform.rotation = Quaternion.Lerp(
this.transform.rotation,
targetOrientation,
Mathf.Clamp01(Time.deltaTime*speed));
}


There's a possibility, depending on how Unity does its rotation math, that rounding errors could creep into this form over time. Try it and see if that occurs. If so, there are some steps we can take to snap the internal state to one of the 24 valid ones and prevent error accumulation.

• Yes! This is what I wanted. In fact the previous code was accumulating errors, in which I could see rotation stopping at around 2.5 degrees instead of getting values in multiple of 90. But I saw such thing with your code only once. Kudos, it is efficient. Your code seems to be engine friendly. :) – nameetemp Jun 1 '15 at 17:19