# How to keep my Quaternion-using FPS camera from tilting and messing up?

I am using an FPS-like camera, and it uses quaternions. But, whenever I try looking up and then sideways, it tilts, and sometimes it can turn upside down. How can I fix this?

• (Will Self-answer in 7 hours)... Jun 15, 2012 at 4:59

You could decompose your quaternion into a yaw/pitch/roll set of angles, but that's overkill usually.

cameraOrientation = cameraOrientation * framePitch * frameYaw;

Try this:

cameraOrientation = framePitch * cameraOrientation * frameYaw;

It will then never generate tilt/roll and is equivalent to storing yaw and pitch separately

• This. This is exactly what I was looking for. Solved my problem in a one-liner. Nice! Apr 18, 2013 at 4:25
• Are framePitch and frameYaw float types? Also, I would be grateful for some clarification on your first sentence. Oct 26, 2015 at 20:00
• @sirdank cameraOrientation, framePitch, and frameYaw are all quaternions (each quaternion is 4 floats or doubles).
– Dan
Dec 6, 2016 at 23:16
• Awesome. This answer maintains the (Euler) rotations order (although one multiplication could be saved if pitch and yaw changes are exclusive like in the @Aeodyn's answer). By multiplying this way, Pitch is accumulated on the left while Yaw is accumulated on the right. It gave me just the hint I needed to understand the true problem of rotation accumulation in the form or a quaternion. Roll is not needed for a FPS cam. But... If Roll was needed, it would need to be accumulated separately. Jul 24, 2020 at 13:07

This is a problem I had for a while, and I couldn't find any answers for, so I thought I would post it here.

It is actually quite simple. How you are most likely doing the rotations is like this:

currentDirection * newRotation;

But, doing it like this doesn't work either.

newRotation * currentDirection;

What you have to do, is do it in the first order for the up and down rotations, and in the second order for the sideways rotations.

For me, it was like this:

if (keyboard.IsKeyDown(Keys.Up))
Direction = Direction * Quaternion.CreateFromAxisAngle(new Vector3(1, 0, 0), TurnSpeed);
if (keyboard.IsKeyDown(Keys.Down))
Direction = Direction * Quaternion.CreateFromAxisAngle(new Vector3(-1, 0, 0), TurnSpeed);
if (keyboard.IsKeyDown(Keys.Left))
Direction = Quaternion.CreateFromAxisAngle(new Vector3(0, 0, 1), TurnSpeed) * Direction;
if (keyboard.IsKeyDown(Keys.Right))
Direction = Quaternion.CreateFromAxisAngle(new Vector3(0, 0, -1), TurnSpeed) * Direction;

For a reason why, the first way has the rotation relative to the current sideways direction, which you want for up and down, but you don't want that for the sideways rotations, which is why the second order is needed.

• I had to wait 7 hours to answer my own... Jun 15, 2012 at 17:00
• sorry, can you explain this with some screenshots? "first order" and "second order" is a bit confusing - thanks! Jun 16, 2012 at 3:26
• When working with Quaternions, order does matter, as it always does with 3D rotations. In my example, "Direction *= newRotation" is the same as "Direction = Direction * newRotation". But, with left and right rotations, we want it the other way around. This makes it not dependent on the up and down tilt, which causes roll. Jun 16, 2012 at 18:37
• Oh I though lt the point of quarternions was that it avoided gimbal lock and the rotations were all independent, but I may have misread the textbook. So just to clarify, are you saying that if you reverse the order of the rotation transformations, you can prevent the camera from tilting? Or are manually setting the roll and pitch transformations to 0 when you rotate yaw? Jun 16, 2012 at 19:45

For an FPS camera you usually don't want roll and are limited to +/- 90 degrees pitch, so I'd just keep track of the current state using yaw and pitch angles. The full power of quaternions isn't really helpful for this.

You can still convert the yaw/pitch angles to and from quaternions in case you want to transition between the FPS camera and animated cameras using quaternion keyframe interpolation, or something like that.

Another simple trick is to put the camera in a GameObject, and have the Yaw rotation control the game object, while the child camera is configured with the Pitch coordinates:

playerCameraHolder.transform.Rotate(0, rotationYaw, 0);
playerCamera.transform.Rotate(rotationPitch, 0, 0);