Need Help With FMath::Lerp, FRotator And SetRelativeRotation

Hello Everyone,

I’m trying to *lerp *from one rotation to another using FRotator and FMath::Lerp function. I clearly do not understand how FMath::Lerp works and I would love if someone could explain it to me. Basically, I want my component to rotate from (0, 90, 0) being (Pitch, Yaw, Roll) to (0, 180, 0). When I do the FMath::Lerp from (0, 90, 0) to (0, 180, 0), the last returned FRotator is (0, 180.3, 0), which looks fine to me. But, when I call SetRelativeRotation and pass (0, 180.3, 0) as an argument, the rotation is set to (0, -179.7, 0).

Here’s the output log of the last 5 ticks of the lerp. The Before SetRelativeRotation is the value of the FRotator passed as an argument to SetRelativeRotation and After SetRelativeRotation is the value returned from GetRelativeRotation after calling SetRelativeRotation.
setRelativeRotation_problem.PNG

To me, this doesn’t make any sense! Can anyone explain this to me please ? Maybe give a quick solution ? Either way, it would be wonderful !

Thank you !

FMath::Lerp blends between two values based on an Alpha. The values you are blending between cannot change as you lerp (well, they can, but it doesn’t give the right effect). Because of the way rotations are handled (they wrap and are normalised) the standard ‘Lerp’ function doesn’t apply to them in the same way.

For Rotators, you would usually convert them to Quaternions and use FQuat::Slerp(), or you can use FMath::RInterpTo to move towards a target rotation at a given speed.

E.g.:



FRotator NewRotation = FQuat::Slerp(Rotation1.ToQuat(), Rotation2.ToQuat(), Alpha);


3 Likes

Thanks for the reply!

Of course! Just to be clear, the arguments I pass in the function call, except the alpha, doesn’t change for the entire lerp.

But, I’ll try with the Quaternion trick. Right now I am using SetRelativeRotationExact and it’s working perfectly. But, in the documentation, it says to use with care since it may not update the rotator cache or whatever it is. Honestly, I didn’t quite understand the side effects of this.

I’ll keep you up to date!

I’m back!

So, I tried with FQuat::Slerp and the result is the same as with FMath::Lerp. Here’s a short clip to show you what happens when the lerp finishes:

[video]https://gyazo.com/a427b3b275ffbbf3d51ec975a3d618c5[/video]

If you look at the rotation in Z in the Details pane, more specifically when the door closes, everything is perfect until the last tick of the door closing where the rotation in Z goes from a positive value to a negative value. I make some calculation in another part of the code that uses the current relative rotation of the component and since Z is flipped, it breaks my calculation.

Using SetRelativeRotationExact fixed my problem, because it doesn’t flip Z.

I assume you are doing something like this every tick:

  • update interpolation alpha [0 … 1]
  • interp between two rotations passing in alpha [rotA rotB]

In our interp movement code on the final tick where alpha becomes <0 or >1 rotation is directly set with SetRelativeRotation(). No longer need to interp because you have passed 0 or 1 and can directly set the target rotation.

I say this because the value “180.3” suggests you are interping to a number outside [0…1] range?

That’s exact. In fact, the Alpha was above 1 at some point, so I tried to clamp it, but I get the same problem.

Just for more information about the door, it has an initial rotation in the Z axis of 180. The open rotation and close rotation of the door are calculated based on an offset. In this case, the offset is -90 in the Z axis. So, the open rotation for the door is (0, 0, 90) and the close rotation is (0, 0, 180). When the door goes from closed (0, 0, 180) to opened (0, 0, 90), nothing goes wrong. But when the door goes from opened (0, 0, 90) to closed (0, 0, 180), that’s when, at the last tick, the door’s rotation goes from positive to negative.

I’ve noticed that as soon as the rotations are greater than 180, they goes from **positive **180 to **negative **180 and starts going from -180 to 0. I really don’t understand this behavior since rotations mostly goes from 0 to 360 before going back to 0.

Well, I found a fix for now. Basically, I have a method called CalculateAnimationProgress which calculate the progress [0…1] of animation going from rotation A to rotation B based on the current rotation. Since the current rotation was flipped, it was outside the bounds of rotation A and rotation B, which resulted in progress to be < 0 or > 1. I just clamped the value between [0…1] and now everything works like a charm.

F.Y.I., CalculateAnimationProgress is used each time the door is opened or closed. Since the player can decide to close the door in the middle of the opening animation, I needed a way to find the animation progress of the opening animation to drive the alpha for the closing animation’s lerp.

Anyway, thanks for your help everybody!

Last thing I’d suggest is look at the FMath interp "ease-in and “ease-out” functions:

https://docs.unrealengine.com/en-US/…Out/index.html

They make your interps a bit more real looking by ramping down the speed at either end of the interp.

Hi, I don’t understand why for me ToQuat() is undefined.

FRotator NewRotation = FQuat::Slerp(AltReleasedRotation.ToQuat(), AltPressedRotation.ToQuat(), value);

To convert Rotator to quaternion, please use:
FQuat4d FRotator3d::Quaternion() const

For example, this is how you set Rotator for your Actor

FQuat NewRotation = FQuat::Slerp(currentRotation.Quaternion(), targetRotation.Quaternion(), delta);
SetActorRotation(FRotator(NewRotation))

delta is a double value between 0 and 1

1 Like