Gimbal lock causing smooth rotational issues

I am creating a 2D game where the character walks around a square planet. When trying to rotate the character, it rotates smoothly from North, West, East and back. The problem arises when I rotate it south from either the west (Euler angle notation: 90, 0, 0) or east (-90, 0, 0) side, the character rotates smoothly and appropriately, however from the rotational value of (0, 180, 180) in the south, it does not properly rotate back to the west or east side. It begins to spaz and jitter like crazy flipping over the Z axis (all rotations are on the Y axis or pitch if I am not mistaken). I first started using Blueprints but now am coding it. This is the simple function that handles rotation. I am misusing the Quaternions somehow I know it. Some said this should work but I am simply converting it back to Euler rotation in SetActorRotation which causes Gimbal Lock. Help would be greatly appreciated as I am at a loss.

void ACodeOrvoCharacter::handleRotation() 
{
	FQuat q;
	switch (gstate)
	{
	case EGravityState::GS_NORTH:
		q = FQuat(0.0f, 0.0f, 0.0f, 1.0f);
		break;
	case EGravityState::GS_EAST:
		q = FQuat(0.0f, 0.707107f, 0.0f, 0.707107f);
		break;
	case EGravityState::GS_WEST:
		q = FQuat(0.0f, -0.707107f, 0.0f, 0.707107f);
		break;
	case EGravityState::GS_SOUTH:
		q = FQuat(0.0f, 1.0f, 0.0f, 0.0f);
		break;
	}
		this->SetActorRotation(FQuat::Slerp(this->GetActorRotation().Quaternion(), q, .25));
}

Hello rantrod!

Thanks for the quick respond! I’ve implemented the Euler angle method that you’ve shown and again the problem is introduced when rotating fully upside down 180 in the pitch. From either the west or east (90 or -90) it begins to rotate to (-77, -180, -180) and flips continuously very quickly between that and (-77, -180, 0).

What you say makes sense and is why this problem drives me insane, I’m only rotating about the pitch. However I just realized I may be rotating about all three. Maybe not all at once but when I rotate for left and right, it is about the Z-axis, but only for North and South orientations. When my character is on the west or east, I rotate about the X-axis for left and right. Anyway, not sure if it’s relevant but I thought it would be noteworthy.

  • Thanks again!

Sure thing!

So adding that line of code eliminated the jittery flipping, but it doesn’t rotate the full 180. Let me get those lines for rotating the character and a screen shot of each world side (NSEW).

float TravelDirection;
	switch (gstate)
	{
	case EGravityState::GS_NORTH:
		TravelDirection = PlayerVelocity.X;
		break;
	case EGravityState::GS_SOUTH:
		TravelDirection = -PlayerVelocity.X;
		break;
	case EGravityState::GS_EAST:
		TravelDirection = -PlayerVelocity.Z;
		break;
	case EGravityState::GS_WEST:
		TravelDirection = PlayerVelocity.Z;
		break;
	}

	// Set the rotation so that the character faces his direction of travel.
	if (Controller != nullptr)
	{
		if (TravelDirection < 0.0f)
		{
			Controller->SetControlRotation(FRotator((180.0f * movementVector).Rotation()));
		}
		else if (TravelDirection > 0.0f)
		{
			Controller->SetControlRotation(FRotator(movementVector.Rotation()));
		}
	}

Also a note that the movement vectors are set according to where the character is.
Ex: if N → MovementVector = 1, 0, 0
E → MovementVector = 0, 0, -1
W-> MovementVector = 0, 0, 1
S → MovementVector = -1, 0, 0

I see your point, I suppose I should’ve put the jump or up vector instead of the movement. Changing it to that begins to flip the camera, I need to restrict rotation for the camera arm to fix that. The south rotation doesn’t change though.

Also another note that I forgot I had some blueprints doing some work for the left right rotation. Here it is.

Also they were connected, I disconnected them to see the effect without them.

No no you’re fine! You’re helping a lot just by taking the time to answer! ^.^

Basically the south rotation is still the same as the picture I posted above regardless of changes. Yeah, I also saw that. Let me give these things a go and see what goes on.

The left right rotations were working fine, I’m wondering if like you said, the set up is messing with the rotations and causing the South to be off.

Ok so an update. Ive been able to completely ignore all code and the left right is now done entirely via the blueprint section posted in the leftright.png above. South rotations are still off though! D:

I believe it is also noteworthy to say that using quaternion Slerp produces the same result and that using SlerpFullPath to avoid shortest path calculations does not fix it.

Also interesting to note…that I got it working, but decided to figure out what I had originally done wrong and broke it again. After some tweaking, I turned off UseControllerRotationYaw and set the player controller camera manager to view pitch, roll, and yaw all from 0 - 360. All this while using this code using quaternions:

void ACodeOrvoCharacter::handleRotation()
{
	float TargetPitch = 0.0f;
	switch (gstate)
	{
	case EGravityState::GS_NORTH:
		break;
	case EGravityState::GS_EAST:
		TargetPitch = -90.0f;
		break;
	case EGravityState::GS_WEST:
		TargetPitch = 90.0f;
		break;
	case EGravityState::GS_SOUTH:
		TargetPitch = 180.0f;
		break;
	}
	FRotator CurrentRot = GetActorRotation();
	CurrentRot.Roll = 0.0f;
	FRotator TargetRot(TargetPitch, CurrentRot.Yaw, CurrentRot.Roll);
	FQuat desired = FQuat::Slerp(CurrentRot.Quaternion(), TargetRot.Quaternion(), .25f);
	//CurrentRot = FMath::Lerp(CurrentRot, TargetRot, .25f);
	this->SetActorRotation(desired);
}

and it worked. My dumb self wanted to see if quaternions were truly necessary so I uncommented the Lerp and changed code accordingly, now I can no longer reproduce the working project.

I’ll say that if it’s a 2D game, you don’t need quaternions and there shouldn’t be any gimbal lock. By your description, you’re probably only changing the pitch of the character (and Yaw for left/right), but in Euler if one of the elements is zero, you won’t get Gimbal lock).

 float TargetPitch = 0.0f;
 switch (gstate)
 {
 case EGravityState::GS_NORTH:
     break;
 case EGravityState::GS_EAST:
     TargetPitch = -90.0f;
     break;
 case EGravityState::GS_WEST:
     TargetPitch = 90.0f;
     break;
 case EGravityState::GS_SOUTH:
     TargetPitch = 180.0f;
     break;
 }
     FRotator CurrentRot = GetActorRotation();
     FRotator TargetRot(TargetPitch, CurrentRot.Yaw, CurrentRot.Roll);
     CurrentRot = FMath::Lerp(CurrentRot, TargetRot, .25f);
     SetActorRotation(CurrentRot);

Note: I didn’t verify the above compiles so sorry if I miss-typed something.

As far as what you have written, I don’t see any glaring errors. Quaternion interpolation finds the shortest route along all axes (pitch, yaw, roll),so you can get unwanted rotations even if your doing everything correctly. I suggest using Euler as described above.

Can you include a screenshot? I don’t want to make wrong assumptions. That said, yes, if all three Euler angles are changing you can see gimbal lock, By your description, the character shouldn’t be rolling though. Could you try adding this line in the code I wrote earlier:

  FRotator CurrentRot = GetActorRotation();
  CurrentRot.Roll = 0.0f; // <-- add this line
  FRotator TargetRot(TargetPitch, CurrentRot.Yaw, CurrentRot.Roll);

And see if anything different happens.
It would also help to see the lines of code used to rotate the character left and right.

I do see this line:

Controller->SetControlRotation(FRotator((180.0f * movementVector).Rotation()));

What is your intention here? Do you mean to add 180 degrees to movementVector.Rotation()? Assuming movementVector is the vector the character moves in, like (1, 0, 0) or (0,0,1) then you would want:

Controller->SetControlRotation(FRotator((-1.0f * movementVector).Rotation()));

Do you mean that when in the south, the character still flips around, or do you mean it’s not turning 180 because of the like that clears the roll? With or without that line, Roll should be zero. Not sure why it doesn’t work.

The camera rotation (angle and elevation) is tied to the controller. Have you tried just doing SetActorRotation instead of Controller->SetControlRotation? You’d probably have to uncheck a box on the character details that says the controller drives the rotation.

PS: sorry I haven’t been able to help more :\