Unreal Engine 4 memo: How to perform rotator interpolation correctly?

In order to let an actor transition from one rotator to another smoothly, function FMath::RInterpConstantTo() should be used. The official documentation on this function is too brief to be helpful. This article aims to clear up the confusion.

Add member function and variable declaration

First off, in the actor class declaration, add the following functions and variables. PerformRotationInterpWithDelay(Delay) is the public interface to be called outside the class. Its purpose is to halt for a duration of Delay, then let the actor rotate smoothly until the target rotator is reached, which in our example is simply FRotator::ZeroRotator.

UCLASS()
class QL_API AMyActor : public AActor
{
    GENERATED_BODY()

public:

    ...

    //------------------------------------------------------------
    // After Delay seconds, perform PerformRotationInterpCallback()
    // which sets bStartRotationInterp to true, and performs rotation interpolation
    // in Tick() until the rotation becomes FRotator::ZeroRotator
    //------------------------------------------------------------
    UFUNCTION(BlueprintCallable, Category = "C++Function")
    void PerformRotationInterpWithDelay(const float Delay);

protected:

    ...

    //------------------------------------------------------------
    //------------------------------------------------------------
    UFUNCTION()
    void PerformRotationInterpCallback();

    //------------------------------------------------------------
    //------------------------------------------------------------
    bool bStartRotationInterp;
}

Define the functions

Implementation detail of PerformRotationInterpWithDelay(Delay) is shown below. After Delay, function PerformRotationInterpCallback() is called, which simply sets the flag bStartRotationInterp, making the actor rotate in the next Tick() call.

For the static function FMath::RInterpConstantTo(), the first argument is the current actor rotator GetActorRotation(), not the initial actor rotator. The second argument is the target actor rotator, which is set to FRotator::ZeroRotator in this example. The third argument is the duration in second between two frames DeltaTime. The last argument is the interpolation speed not adequately explained in the documentation. Here is what it actually represents: For each call of FMath::RInterpConstantTo(), the pitch, yaw, roll of the actor are uniformly incremented by a value of k. k is first set to the difference between target rotator and current rotator, but then clamped by [-m, m], where m is equal to DeltaTime times the interpolation speed. An interpolation speed of S indicates a change in pitch, yaw, roll by S degree per second. FMath::RInterpConstantTo() returns the new rotator to be applied to the actor per Tick(). To take into account the finite precision of floating point arithmetic, currentRotation.Equals(targetRotation) is subsequently performed. If it evaluates to true, the flag bStartRotationInterp is unset, and the rotation stops.

//------------------------------------------------------------
//------------------------------------------------------------
void AMyActor::PerformRotationInterpWithDelay(const float Delay)
{
    GetWorldTimerManager().SetTimer(StartRotationDelayTimerHandle,
    this,
    &AMyActor::PerformRotationInterpCallback,
    1.0f, // time interval in second
    false, // loop
    Delay); // delay in second
}

//------------------------------------------------------------
//------------------------------------------------------------
void AMyActor::PerformRotationInterpCallback()
{
    bStartRotationInterp = true;
}

//------------------------------------------------------------
//------------------------------------------------------------
void AMyActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

    ...

    // interp rotation
    if (bStartRotationInterp)
    {
        FRotator NewRotation = FMath::RInterpConstantTo(GetActorRotation(), FRotator::ZeroRotator, DeltaTime, 100.0f);
        SetActorRotation(NewRotation);

        if (GetActorRotation().Equals(FRotator::ZeroRotator))
        {
            bStartRotationInterp = false;
        }
    }
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s