Quaternion solution to mouse look pitch and yaw

I am using a custom MovementComponent where my character aligns to a surface based on changing gravity direction.

The character aligns perfectly to the surface, but I can’t get my quaternion solution to my mouse look working. The idea is that regardless of the world orientation of the character, the mouse look happens in local space. To further explain, the character follows the normal 1st person mouse look rules regardless of the world orientation the character has.

(I’ve disabled the camera locks - Yaw and Pitch are not dealt with in CameraManager::LimitViewPitch and LimitViewYaw,until I can figure the movement out first).

What I’ve tried so far is to replace MyPlayerController::AddPitchInput and MyPlayerController::AddYawInput with a quaternion solution as such:


FQuat ActorRightQuat = FQuat(MyPawn->GetActorRightVector(), 0);
FQuat RotationQuat = FQuat(0, 0, Val, 0);
ActorRightQuat.Normalize();
RotationQuat.Normalize();
FQuat FinalQuat = ActorRightQuat * RotationQuat;
FinalQuat.Normalize();
RotationInput += FinalQuat.Rotator();

That results in a weird jumpy thing. The view rotates 180 degrees in pitch and so the solution doesn’t work. I’ve tried diminishing the value by multiplying it with 0.1f, but it doesn’t seem to get any better. What I’m trying to do with the code, is to get the actor right vector to pitch the character view by the amount of the rotation value around this vector. Also tried to create quaternion from the right vector and the pitch input value (FQuat(ActorRightVector, InputValue)) and using Quaternion.Rotator() to add to the RotationInput, but that results in nothing that works.

I tried several approaches when replacing the CameraManager::ProcessViewRotation. Here is one (that doesn’t work:

 
FQuat DeltaRotQuatYaw = FQuat(MyPawn->GetActorUpVector(), OutDeltaRot.Yaw);
FQuat DeltaRotQuatPitch = FQuat(MyPawn->GetActorRightVector, OutDeltaRot.Pitch);
FQuat OriginalQuat = FQuat(ActorUpVector, 0);
DeltaRotQuatYaw.Normalize();
OriginalQuat.Normalize();
DeltaRotQuatPitch.Normalize();
OriginalQuat = OriginalQuat \* DeltaRotQuatYaw;
OriginalQuat.Normalize();
OriginalQuat = OriginalQuat \* DeltaRotQuatPitch;
OriginalQuat.Normalize();
FRotator NewRotation;
NewRotation = OriginalQuat.Rotator() * InputScale;
OutViewRotation += NewRotation;

I’ve tried so many things with quaternions, but I can never get this to work. I’d think this was INCREDIBLY simple, but I’ve been at it for a month now with little luck. A bit frustrating. I’ve printed data on the screen that display my inputs, and it seems that everything else is correct (right vectors, up vectors, gravity vectors, inputs etc.), but my quaternion math.

And finally an apology: I posted a similar question some weeks ago, but got no replies, so I thought I’d try to post it again with what I’ve learned so far.

1 Like

I ran into a lot of trouble here myself! Have you tried ditching the MyPlayerController::AddPitchInput and MyPlayerController::AddYawInput functions and just setting the controller transform or rotation directly? If you want 6DoF, I’ve found you should just avoid those input functions.

Haven’t tried that yet. Seems a few others have had success with that as well. I was trying to stay away from total 6DoF, because I thought it would get rid of the gimbal lock altogether. I want to keep it so that even though my character is at a non-standard rotation vis-a-vis world axes, it would still have gimbal locks locally.

I don’t know if that’s clear at all, but I want to keep local gimbals, so even though my character’s world orientation is non-standard (= not in the XY-plane) I still get gimbal locks when I look up or down - to keep the 1st person shooter feel.

I might try it though, as I’m not sure if I’m correct in my assumption.

Yeah, I think understand what you’re saying. Even if you want to keep the local Gimbal Locks, you’ll need to implement them outside of using the AddPitchInput & AddYawInput functions. There are too many assumptions/limitations made with those functions. My suggestion:

  1. Apply all rotation logic to the Character, not the Controller (note you’ll probably want to slam the Controller rotation to be the same as the Character rotation after applying the new delta every frame, cause other systems are probably relying on that).
  2. Implement your own Gimbal Lock restrictions based on the player’s local space.

Ok, that seems to actually be quite a logical solution. Let me try that for the next few days and see what I can come up with.

Thanks rcdarcey!

Of course! Good luck :slight_smile:

Okay, I got this working now, but there is a bit of a jitter when walking on a surface that updates gravity.

As it turned out, it was really simple. And I mean too simple. I didn’t know there was a function called AActor::AddActorLocalRotation(). I ended up using that on the AddYawInput to apply the Yaw to the character itself. For the pitch I had to have a long think, because the gravity code updates the character CapsuleComponent each turn. So I just applied AddLocalRotation on the CameraComponent. Works well now, and I have my local gimbals. I think the function is why the jitter happens, I might have to do a LERP for the movement so walking on surfaces that update the gravity would be smoother.

For the PlayerCameraManager::ProcessViewRotation I just ended not adding delta, but getting the rotation of my pawn put in as the OutViewRotation.

Also I have to put back in the PlayerCameraManager::LimitPitchView, as I want the character not able to turn upside down…I might just put the limiter into the AddPitchInput() function and not bother doing anything with the camera manager.

I know this post is pretty old but I face the same problem and I wanted to stick with the controller rotation approch. I manage to do so, so I’ll share what I’ve done.

In the PlayerCameraManager::ProcessViewRotation ( I inherited this class and override the function) I multiply the GravityToWorld quaternion on the OutViewRotation applied the deltaRot and clamp the OutViewRotation. Then I multiply the WorldToGravity quaternion to OutViewRotation and this work like a charm for solo game or server instance of a player.

The quaternion are inside the CharacterMovementComponent.

I’m still figuring out how to do this client side XD. Hope it helped someone