Use Quaternion.RotateTowards() instead of Quaternion.Slerp()

This lecture [#21 Rotate to Face Target] provides code which incorrectly uses Quaternion.Slerp() to slowly rotate the enemy toward the desired rotation. That method, for the last parameter, takes a number between 0.0 - 1.0 for an interpolation ratio between rotation A and rotation B [with 0.0 corresponding to all A, and 1.0 all B, and other values linearly interpolated between].

The correct method to use here is Quaternion.RotateTowards() which for the 3rd parameter instead takes a maximum angle of rotation in degrees (which should be a turnSpeed [in degrees per second] multiplied by Time.deltaTime).

What code you propose to use then, the only difference seems to be the speed of the rotation?

I know its kinda dead topic, but if anyone doing the course 2 years later wanders what is the difference and why Quaternion.RotateTowards() is the “proper” way of doing it. If I understand this correctly:

Quaternion.Slerp() rotates in percentage between 2 rotations, so it will not provide constant rotation speed for changing source and destination rotations (especially for moving target). If the targets moves faster, so will the rotating object rotate faster, always to meet the desired % of given rotation.
For example, if speed is set to say 25% per frame, If the target moves 100 degrees in 1 frame, our object will rotate by 25 degrees. However, if the target moves by 50 degrees, our object will rotate by 12,5 degrees.
Quaternion.RotateTowards() rotates in limited rotation speed per step (i.e. degrees per second), so if we set the limit to 25 degrees, our object will rotate always with the same angular speed.

In this case, I think it makes no visible difference, as calculation is too fast and rotation changes for each frame are too small to notice.

The code you want to use here is basically the same as in course lecture:

transform.rotation = Quaternion.RotateTowards(transform.rotation, lookRotation, Time.deltaTime * turnSpeed);

So basically, what I understand is:
if you want to turn something from known rotation to known rotation - use Quaternion.Slerp() - for example opening a door or chest lid.
If you want to turn towards something with constant angular speed - use Quaternion.RotateTowards() - for example you want to turn the turret of your tower in tower defense type game.

The other issue I have with Lerp/Slerp is it is generally implemented in a way that is not frame rate independent, despite being scaled by deltaTime.

Take, for example:
myFloat = Mathf.Lerp(myFloat, targetValue, 0.5 * Time.deltaTime);

To keep the math simple, if you have a frame rate of 1.0fps, after one second that calculation will have run once, giving you a result that is 50% of the way to the targetValue.

On a faster computer, running at 2fps, that function will calculate twice: the first moving it 25% toward the final value, the second moving just 18.75% of the original difference (because the Lerp is performed against the result of the first frame, with a difference that’s now only 75% of the original). In the same time, the faster computer will only move the value 43.75% of the way to the targetValue.

At real frame rates, the differences are not as drastic, but it is still not independent. In a multiplayer environment, different clients would be out of sync.

You can fix that by accumulating deltaTime from the start of the event and doing Lerp only between the original unmodified values using the total delta since start to scale the ratio. But, at that point, you’ve replicated what MoveTowards is already doing. For times when you want a value that smoothly accelerates or slows to a stop, there are SmoothDamp and SmoothDampAngle functions that work similarly, or assets like DOTween that provide additional tweening functions.

1 Like

Privacy & Terms