- Home /
How do you smoothly transition/Lerp into a new rotation?
How do you smoothly transition or lerp a rotation of an object to a new rotation?
My direction changes every time the player presses a button. But when it does, the rotation gets changed immediately, with no transition whatsoever.
Is there a way I can do this?
Here's the script I'm using, it's pretty straight forward and simple:
GameObject.FindWithTag("Player").transform.eulerAngles = rot;
rot = new Vector3 (direction.y * 100, 0, direction.z * 100);
I just couldn't find a way to smoothly transition Player's eulerAngles to rot since I'm pretty new to Unity and C# itself.
Vector3.Lerp doesn't seem to work as well with it. It flips out and have no idea why. Here's the script I made for it:
GameObject.FindWithTag("Player").transform.eulerAngles = Vector3.Lerp(Vector3.zero, rot, Time.deltaTime * 5);
With this one, for some reason, the rotation just keeps going back and forth. For example, when I'm pressing the right button, it rotates to right, goes back to Zero, then rotates back to right again, then rotates back to Zero, then rotates back to the right again... at a somewhat fast rate until I let go of the right button.
Maybe I did something wrong with it.
Thanks to anyone who can help :)
Try this:
GameObject.FindWithTag("Player").transform.eulerAngles = Vector3.Lerp(GameObject.FindWithTag("Player").transform.eulerAngles, rot, Time.deltaTime * 5);
Every time you lerp with Euler angles, a fairy experiences Gimbal Lock.
Answer by DMGregory · May 30, 2014 at 12:46 AM
Euler angles are okay for data entry, but lousy for interpolation.
For example, if you wanted to blend between Euler angles (0, 0, 0) and (0, 359, 0), the shortest path is to rotate -1 degrees around the y-axis. But interpolating the two vectors will have your object spin around the long way, +359 degrees.
Euler angles also exhibit gimbal lock and other interactions between the rotation axes, so when you try to interpolate rotation on multiple axes at once the object can appear to lurch and tumble from one rotation to another, instead of performing one smooth continuous rotation. Vector3.Slerp won't help here, at least not directly, because it's for interpolating vectors representing spatial directions, not Euler angles.
Quaternion methods solve all of this automatically, because they don't experience gimbal lock or 0/360 wrap-around issues.
Here you can see the same rotation interpolated with Quaternions (left, smooth & predictable) and Euler angles (right, object tumbles along its long axis during the turn) (Source and Interactive Demo)
Working with quaternions in Unity is easy thanks to convenience methods like Quaternion.RotateTowards, which you can use something like this:
// Maximum turn rate in degrees per second.
public float turningRate = 30f;
// Rotation we should blend towards.
private Quaternion _targetRotation = Quaternion.identity;
// Call this when you want to turn the object smoothly.
public void SetBlendedEulerAngles(Vector3 angles)
{
_targetRotation = Quaternion.Euler(angles);
}
private void Update()
{
// Turn towards our target rotation.
transform.rotation = Quaternion.RotateTowards(transform.rotation, _targetRotation, turningRate * Time.deltaTime);
}
This will give you a smooth rotation toward your target orientation, from any starting orientation, at a constant rate of rotation. Note that you can still provide your target orientation using Euler angles, and we can convert that input to an intermediate quaternion representation with one line.
There is a problem with this approach: I tried to rotate an object from (250, 180, 0) to (260, 180, 0) and I used Quaternions as in your example but the result was (260, 0, 180) which was not same as (260, 180, 0)...because the object pivot is not the same as the object center.
The little quaternion demonstration game you made is fascinating. Thank you very much for this work!
Answer by rutter · May 30, 2014 at 12:51 AM
The first problem is that you're using Lerp incorrectly.
Lerp, short for “linear interpolation” does one very simple thing: given two values, x and y, it returns a value that is t percent between them. If you expect the output to change, the arguments you pass in need to reflect that!
It doesn’t make sense to just pass in Time.deltaTime, because that’s only the time that passed during the most recent frame. If your game is running at a constant 50fps, that’s always going to be 0.02.
You need to pass in a value that changes over time.
Suppose we create two variables:
lerpTime
is the number of seconds we want the Lerp to take.currentLerpTime
is the number of seconds since we started the Lerp.
When we start the Lerp, we could set lerpTime
to 5
and currentLerpTime
to 0
:
lerpTime = 5;
currentLerpTime = 0;
We can increase currentLerpTime
by Time.deltaTime
once per frame:
//in Update()
currentLerpTime += Time.deltaTime;
Then, we Lerp like this:
Vector3 result = Vector3.Lerp(a, b, currentLerpTime / lerpTime);
Second, if you're able to, you might consider using Slerp, instead. If Euler angles are working for you, go ahead and use them. There are a few corner cases where they have limits, though.
Answer by davidnibi · Nov 03, 2019 at 07:21 PM
Quaternion.RotateTowards(<existing rotation>, <new rotation>, ...
So simple! I was doing this through about 5 lines! And I was putting off fixing this for a while!
I've translated between a predefined 'light sway' effect on trees (under normal conditions), to a violent effect in a storm!
{
Quaternion newEulerAngle = Quaternion.Euler(finalAngleX + xRotationOffset, finalAngleY + yRotationOffset, finalAngleZ); //Apply new angle to object
transform.rotation = Quaternion.RotateTowards(transform.rotation, newEulerAngle, turningRate * Time.deltaTime);