I’m working on a simple spider physics type movement. I had the following coded up in Blueprints using rotators and things work fine until the pawn reaches 90º pitch. As I understand it, rotators have gimbal lock when working at > 90º of pitch. I translated the Blueprint to C++ in order to use quaternions. However, I realized I do not know how to use quaternions.
I’ve poked at this for a bit using the various related threads but haven’t come to a simple method of taking the impact normal from a trace and rotating the pawn to “stand” flat on that point while respecting the pawn’s yaw using only quaternions.
Here is my rotator based code called on tick:
bool
AKJ_RotatingPlayerController::PositionOnSurface(float DeltaSeconds)
{
APawn * Pawn = GetControlledPawn();
if (!Pawn)
return false;
FTransform PawnTransform = Pawn->GetTransform();
FQuat PawnQuat = PawnTransform.GetRotation();
FVector PawnUpVector = PawnQuat.RotateVector(FVector(0, 0, 1));
FVector PawnRightVector = PawnQuat.RotateVector(FVector(0, 1, 0));
FVector PawnForwardVector = PawnQuat.RotateVector(FVector(1, 0, 0));
FVector PawnLocation = PawnTransform.GetLocation();
FVector Start = PawnLocation + PawnUpVector * TraceStartFactor;
FVector End = PawnLocation + PawnUpVector * TraceLength;
FCollisionQueryParams Params(true);
Params.AddIgnoredActor(Pawn);
FHitResult OutHit;
GetWorld()->LineTraceSingle(OutHit, Start, End, ECC_Visibility, Params);
if (OutHit.bBlockingHit)
{
FVector ImpactNormal = OutHit.ImpactNormal;
FVector ImpactPoint = OutHit.ImpactPoint;
DrawDebugDirectionalArrow(GetWorld(), Start, ImpactPoint, 40, FColor::Green, true, 5);
FRotator FromYZ(FRotationMatrix::MakeFromYZ(PawnRightVector, ImpactNormal).Rotator());
FRotator FromXZ(FRotationMatrix::MakeFromXZ(PawnForwardVector, ImpactNormal).Rotator());
FRotator PawnRotator = PawnQuat.Rotator();
FRotator FinalRot = FRotator(FromYZ.Pitch, PawnRotator.Yaw, FromXZ.Roll);
FVector NewLocation = ImpactPoint + ImpactNormal * 100.0;
PawnTransform.SetRotation(FinalRot.Quaternion());
PawnTransform.NormalizeRotation();
PawnTransform.SetLocation(NewLocation);
Pawn->SetActorTransform(PawnTransform);
return true;
}
return false;
}
This code and the blueprint behaved the same way. Movement works as expected < 90º and I can move straight up 90º and rotate in-place, but any other sort of movement will tend to send it all a bit haywire. Quite frequently a drift will be introduced and the pawn will mis-trace/mis-align itself with no control input until it reaches the ground (I turn on gravity and zero out the rotation if a trace fails) where it will stop drifting but will have a slight jitter. At this point it will start drifting again on any º of incline, so something gets permanently tweaked.
Can anyone shed light on what the problem is? If this is gimbal lock shenanigans, how would I best take that impact normal and rotate my pawn accordingly using only FQuat?
-Kyle