Download

Why does my code not allow me to interpolate in 90 degree increments when using quaternions?

I am trying to rotate an actor by 90-degree increments when the user presses a key. I have used QInterpConstantTo to do this, and it works fine in two axes, but on the third, I am still getting what I think is gimbal lock as the interpolation never completes. I am already using quaternions, so I don’t know what I am doing wrong? How can I prevent this when rotating an actor by 90 degrees in either of the 3 axes at one time? below is example code that causes the problem, when i press the key that calls the 'ToggleRotateY function, the actor rotates twice then locks on the third attempt and no more input is accepted

Header File:



#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "CubePawn.generated.h"

UCLASS()
class ROTATEMATCH_API ACubePawn : public APawn
{
GENERATED_BODY()

public:
ACubePawn();

public:
virtual void Tick(float DeltaTime) override;
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

private:
void ToggleRotateY();

bool shouldRotate = false;
float RotationSpeed = 10.0f;
FQuat QuatTarget = FQuat(0.0f, 0.0f, 0.0f, 0.0f);

UPROPERTY(EditAnywhere)
UStaticMeshComponent* PawnMesh;
};


CPP File



#include "CubePawn.h"

ACubePawn::ACubePawn(){
PawnMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Pawn Mesh"));
PawnMesh->SetupAttachment(RootComponent);
RootComponent = PawnMesh;
}

void ACubePawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent){
   Super::SetupPlayerInputComponent(PlayerInputComponent);
   PlayerInputComponent->BindAction("RotateY", IE_Pressed, this, &ACubePawn::ToggleRotateY);
   }

void ACubePawn::Tick(float DeltaTime){
   Super::Tick(DeltaTime);

   if (shouldRotate) {
      FQuat NewRotation = FMath::QInterpConstantTo(GetActorRotation().Quaternion(), QuatTarget, DeltaTime, RotationSpeed);
      SetActorRotation(NewRotation.Rotator());

      if (GetActorRotation().Equals(QuatTarget.Rotator(), 0.1f)) {
        UE_LOG(LogTemp, Warning, TEXT("Reached Target"));
        shouldRotate = false;
        SetActorRotation(QuatTarget.Rotator());
      }
   }
}

void ACubePawn::ToggleRotateY() {
   if (!shouldRotate) {
      QuatTarget = GetActorRotation().Quaternion() * FRotator(90.0f, 0.0f, 0.0f).Quaternion();
      shouldRotate = true;
   }
}