- Home /
Quaternion.Slerp in a coroutine
Hi, I'm having an issue with the timing of a rotation. I have a coroutine which translates an object and at the same time I want to rotate it smoothly for as long as it's moving.
public IEnumerator MoveToPosition (Vector3 newLocalTarget, float time)
{
float elapsedTime = 0.0f;
Vector3 startingPosition = transform.localPosition;
Quaternion targetRotation = Quaternion.Euler ( new Vector3 ( 0.0f, 0.0f, 200.0f ) );
while (elapsedTime < time) {
transform.localPosition = Vector3.Lerp (startingPosition, newLocalTarget, (elapsedTime / time) );
// Rotations
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, (elapsedTime / time ) );
elapsedTime += Time.deltaTime;
yield return new WaitForEndOfFrame ();
}
Debug.Log ("Translation and rotation done! ");
yield return 0;
}
The translation works as expected. However, the rotation rotates to the desired angle, but finishes rotating around half the time it takes the object to translate. Ideally I want the rotation and translation to end at the same time. I've tried using Quaternion.Lerp in place, but the same problem exists. Can anyone spot the issue? Is there any other way so the rotation completes at the same time as the translation?
Many thanks
Answer by aldonaletto · Dec 16, 2011 at 12:21 PM
You should have a startingRotation variable to tell what's the initial rotation. You should also move the elapsedTime increment to the beginning of the while to ensure that the final position/rotation will be reached before the while ends:
...
Vector3 startingPosition = transform.localPosition;
Quaternion startingRotation = transform.rotation; // have a startingRotation as well
Quaternion targetRotation = Quaternion.Euler ( new Vector3 ( 0.0f, 0.0f, 200.0f ) );
while (elapsedTime < time) {
elapsedTime += Time.deltaTime; // <- move elapsedTime increment here
transform.localPosition = Vector3.Lerp (startingPosition, newLocalTarget, (elapsedTime / time) );
// Rotations
transform.rotation = Quaternion.Slerp(startingRotation, targetRotation, (elapsedTime / time) );
yield return new WaitForEndOfFrame ();
}
...
NOTE: You're using localPosition, but not localRotation. Is this an intended feature? Mixing local and non-local position/rotation may give weird results.
Argh! Of course, right on all 3 counts (didn't mean to mix localposition and non local rotation). Thanks so much!
This is still useful and works great. Another answer was using a damper variable in the last parameter of Slerp. Seeing this answer made Slerp make so much more sense. $$anonymous$$y character now rotates the entire direction I want in the exact time, adjusting speed to fit the time requirement. Thank you from 2020!
Answer by leonardo_try · Dec 16, 2011 at 11:23 AM
try putting just the elapsedTime in the Lerp and Slerp instead of (elapsedTime / time) and to get a linear movement on the translation, use transform.localPosition where starting position is
Thanks for the response, but putting in elapsed time rather the elapsedTime/ time will just lock the movement to 1 second. Having (elapsedTime / time) allows me to specify a time for the movement.
I don't want to offend you. I just want to clear up some facts. ;)
Generally there are two common uses of lerp. The first one is how it's ment to be used by giving a fix start and end point and move the t value slowly from 0 to 1. This will give you a true linear interpolation.
The second use of lerp is to do a decelerated movement by using the current position as start (which changes each time) and a small constant t value. This is what happens: If you use 0.2 as t value (just as example) It will move 20% of the remaining distance each step. Since the remaining distance will get smaller each frame the amount of movement is getting smaller which cause the slow-down effect. If you start at 100 and want to reach 0 it would be like this:
frame, current V, remaining distance(RD), 20% of RD
1 100 100 20
2 80 80 16
3 64 64 12.8
4 51.2 51.2 10.24
5 40.96 40.96 8.19
6 32.77 32.77 6.55
...
Usually you multiply your constant with Time.deltaTime to be frame independend, but it doesn't really work. Example: If the above example is done at 1 frame per second (just as example) the delta time value would be 1.0 and you would get exact the result above.
But when you have 4 frames per sec (deltatime of 0.25) scaling down the t-constant linearly by 0.25 doesn't work as expected:
frame, current V, remaining distance(RD), 20% of RD
1 100 100 5
2 95 95 4.75
3 90.25 90.25 4.5125
4 85,7375 85,7375 4.2869
5 81,4506
So at 1 fps after 1 sec we have a value of 80. At 4 fps after the same time (1 sec == 4 frames) we have a value of 81.4. This "error" getting even worse when your frame rate gets higher. So as a result it will get faster when the framerate is lower.
If you want an accelerated / decelerated movement that happens in a specific time span, use a Hermite interpolation
Your answer