- Home /
Rotation direction in coroutine
I have a weird problem concerning rotating objects. The object in question is shaped like a plate and positioned horizontally. Now I want to flip it over and turn it into vertical position at the same time. Basically, I want its rotation to go from Euler(90,0,0) to Euler(270,90,0). My problem is, I want it to go there in the obvious direction, increasing both angles, but it rotates the other way. I don't want to use animations, this is not the only rotation I'll have long term and a coroutine just seems better suited.
I've tried quaternion.slerp, quaternion.lerp, vector3.lerp in combination with Euler and eulerAngles as well as splitting the rotation into two parts from start to the midway rotation and from there to the target. It keeps rotating in the wrong direction. I've also tried using (-90,90,0) as the target, also when I try to change the angles at runtime in the inspector, it keeps converting them to something strange... Debug.log tells me the Euler angles are right, but it either converts them wrong when going to a quaternion or does something I have no idea of. I'm really clueless, what am I doing wrong?
Answer by robertbu · Jun 16, 2013 at 11:33 PM
There are two issues here. The first is that Quaternion.Slerp() takes the shortest distance between the two rotation, so it will not go around the long way. The second issue is there are multiple euler angle representations for any "physical" rotation, so you cannot depend on Unity using any specific one. As you found out, you can set an angle to something like (180,0,0), and immediate read it back and get (0,180,180) (which is the same physical rotation).
One solutions is to manage the angle yourself. That is to create and use your own Vector3 and treat Transform.eulerAngles as "write-only." Never read eulerAngles and expect any particular representation. Since you never cross the 0/360 boundary for your target angles above, you can do something like:
var v3Rot = Vector3(90,0,0);
var v3Dest = Vector3(270,90,0);
var speed = 4.0;
function Start () {
transform.eulerAngles = v3Rot;
}
function Update() {
v3Rot = Vector3.MoveTowards(v3Rot, v3Dest, speed * Time.deltaTime);
Debug.Log(v3Rot);
transform.eulerAngles = v3Rot;
}
I've tried something similar, only using eulerAngles to get my initial Vector3s, it didn't work and still turned the wrong way. Here's what I did:
transform.rotation = Quaternion.Euler(90,0,0);
StartCoroutine("RotateTo", Quaternion.Euler(270,90,0));
function RotateTo (target : Quaternion) {
var start : Vector3 = transform.rotation.eulerAngles;
Debug.Log(start); // This correctly showed (90,0,0)
var vtarget : Vector3 = target.eulerAngles;
Debug.Log(vtarget); // This also showed (270,90,0) as intended
var i : float = 0.0;
var eulerlerp : Vector3;
while (i<1.0) {
i += Time.deltaTime;
eulerlerp = Vector3.Lerp(start, vtarget, i);
Debug.Log(eulerlerp); // This showed the correct angle as well
transform.rotation = Quaternion.Euler(eulerlerp);
yield;
}
transform.rotation = target;
}
Is the problem my use of Quaternion.Euler? All Debug.Logs showed the intended angle, but the angles shown in the Inspector don't match and the actual rotation of the object is also wrong, it still rotates to (0,45,0), then to (-90,90,0) which is the same correct target rotation, but the wrong way to get there.
This code should work - unless you want to go in the direction (90,0,0) to (-90,90,0). Are you sure that RotateTo is called only once? If called multiple times, the resulting behaviour will be very weird - each time you call RotateTo, a new coroutine instance starts to run, and all of them will fight for the control of the object. Sometimes is better to make sure of this with a boolean variable:
var rotating = false;
function RotateTo(...){
if (rotating) return; // abort other calls while rotating
rotating = true; // I'm start rotating now
... // your code
rotating = false; // rotation finished
}
@Scenia - You cannot use a Quaternion to pass the eulerAngles! That was one of the main point of my original post. You cannot read back eulerAngles and expect any specific value. For this to work, you have to manage the angles in your own Vector3, and never read back eulerAngles form a Quaternion.
Ah, I see. Do I have to do that consistently in the entire project? So I basically don't use transform.rotation and transform.localRotation at all? Also, it should still work when I roll over 0°, shouldn't it? The only problem is when I get over 360, which never happens since I set the angles. Or does transform.eulerAngles only take positive values?
aldonaletto - Good point. In this specific case, I want new coroutines to replace old ones, so I always use StopAllCoroutines() before StartCoroutine().
There are lots of good uses for Quaternions, but you cannot depend on the return value being any particular euler representation. So whether to use them will depend on the rotation problem you are trying to solve. In theory, you could unwind and normalize the return values to solve your problem, but it is far easier to just keep your own Vector3. As for the 0/360 boundary, Vector3.Lerp() will treat the values you enter literally. So if you want to go from 350 degrees to 20 degrees the short way, you just change the representation to go from 710 degrees to 680 degrees. Also since it treats numbers literally, you can make multiple turns...go from 0 to 1080 for three turns.
Answer by abrown4887 · Dec 03, 2013 at 09:29 AM
I'm not sure if I'm answering your question or not but if you are having problems with the rotation taking the long way instead of the short way you might check out this question on SO about determining rotation direction
Your answer
Follow this Question
Related Questions
Rotate object to target position 1 Answer
Lerp 180 degrees while holding B and lerp back 180 degrees when let go of B. 2 Answers
LookRotation Vector3 is Zero, Yet Slerp Still Rotates? 2 Answers
Quaternion based slerp to make homing projectile doesn't turn 1 Answer
Unity thinks my Quaternion is a Float 2 Answers