I am developing a 3D space shooter and I've found that when trying to rotate the ship using X and Y axes, it also rotates on the Z axis.

The answer here helped me a lot, but now when I try to rotate on 3 axes I have the same problem on the 2 axes sharing local space.

The code I have right now is this:

    float rotZ = 1;
    float rotLeftRight = Input.GetAxis("Mouse X") * mouseSensitivity;
    float rotUpDown = Input.GetAxis("Mouse Y") * mouseSensitivity;
    transform.Rotate(0, rotLeftRight, 0, Space.World);
    transform.Rotate(-rotUpDown, 0, 0, Space.Self);
    transform.Rotate(0, 0, -rotZ, Space.Self);

Does anyone know how to make all 3 rotations independent from each other?

  • 2
    \$\begingroup\$ As explained in that answer: you cannot. The idea that rotation is separable into three independent angles is a fiction, not part of three-dimensional reality. This is not part of the engine, it's math & geometry itself against you here; you can't square that circle. So, let's attack this from a different angle: what should the player experience of this feature be? Try to explain it without any reference to angles or independence, but just to how objects move relative to other reference objects in the player's view. Then we can try to find a way to reach, or approximate, that experience. \$\endgroup\$
    – DMGregory
    Commented Sep 6, 2019 at 13:16
  • \$\begingroup\$ Well what I want is that the player is able to rotate the ship's position up and down (pitch) and also right and left (yaw) in order to navigate and fight. And I want to be able to roll over itself mostly to dodge. \$\endgroup\$
    – Hector
    Commented Sep 6, 2019 at 16:59
  • \$\begingroup\$ Your comment and title describe two axes of rotation, your question body text and code show three. How does this third axis figure into the player experience? \$\endgroup\$
    – DMGregory
    Commented Sep 6, 2019 at 17:02
  • \$\begingroup\$ Initially I only rotated the X and Y axes (yaw and pitch) with the mouse to rotate the ship, which moves only forward and changes the direction using the rotation. I had the same issue with those 2 axes but I fixed it rotating one in global space and other in local. Now I want to roll the ship (mostly for combat) but I have the same problem that I had with the othe two, but since there are only 2 spaces, I don't know how to fix it. \$\endgroup\$
    – Hector
    Commented Sep 6, 2019 at 17:21
  • \$\begingroup\$ So try to tell us more about how you expect the controls to work from the player's point of view when using these three different actions in combination. \$\endgroup\$
    – DMGregory
    Commented Sep 6, 2019 at 17:22

1 Answer 1


As I mention in the linked answer, for full 6 DoF controls like spaceflight or swimming, it often works well to think of a corrective twist that you gradually apply to reassert your desired horizontal plane.

Here's one way we can do this:

First, we'll track a normal for the plane we want the ship to orient to when the player is not deliberately rolling.

Vector3 _planeNormal;

void Start() {
    _planeNormal = transform.up;
    // ...

Next, we'll think of our yaw and pitch controls as rotating only our forward vector. We'll play with the up vector next so we can control the twist:

float rotLeftRight = Input.GetAxis("Mouse X") * mouseSensitivity * Time.deltaTime;
float rotUpDown = -Input.GetAxis("Mouse Y") * mouseSensitivity * Time.deltaTime;

Quaternion yawPitch = transform.rotation * Quaternion.Euler(rotUpDown, rotLeftRight, 0f);
Vector3 newForward = yawPitch * Vector3.forward;

Next we're going to look at how we're currently oriented toward our reference plane.

float alignment = Vector3.Dot(transform.up, _planeNormal);
Vector3 referenceUp = Mathf.Sign(alignment) * _planeNormal;

This gives us the perpendicular to our reference plane that's closest to our current vertical direction (to avoid a nasty flip/lurch if we fly in a loop-de-loop - we'd rather tolerate flying upside-down than doing a sudden snap upright)

If we snap straight to this up vector, we can still get a nasty lurch when we're flying nearly perpendicular to our plane - its projection on our view can swing from pointing up to sideways to down and back from just small wiggles in our heading. So instead, we'll blend toward it gradually, with a strength that falls off to zero when we're perpendicular to the reference plane:

Vector3 targetUp = Vector3.Lerp(transform.up, referenceUp, alignment * alignment);

(Note that you'll want to do this at a consistent framerate, or adjust the above for deltaTime if your framerate is variable)

Now we're ready to set our new rotation based on our yawed/pitched forward vector and our calculated up vector:

Quaternion newOrientation = Quaternion.LookRotation(newForward, targetUp);

Before you apply this, you can add in your roll:

float roll = Input.GetAxis("Roll");
if(Mathf.Approximately(roll, 0) == false) {
    newOrientation = newOrientation * Quaternion.Euler(0f, 0f, roll * rollSpeed * Time.deltaTime);

    // Save our new reference plane, according to the player's input.
    _planeNormal = newOrientation * Vector3.up;

transform.rotation = newOrientation;

At this moment, the player has told you "I want my horizon to be rotated like this," so we save that information into our reference plane normal. Wherever possible, we'll try to level-out the ship's orientation to align with this newly-twisted plane, until the player manually twists it back.

(Of course, you can also add a "twist decay" over time, if you want the roll to unroll itself if the player doesn't touch the roll controls for a while).

All together, this lets the player's local yaw/pitch/roll win in the short term, so the player immediately sees consistent response to their controls, and it's always correct relative to their current orientation - even if it introduces an unwanted twist globally. Then as we can, we gradually hint the ship back toward the last horizontal plane the player told us was good for them by deliberately rolling toward it.

  • \$\begingroup\$ Thank you! I missed that part of the explanation in the attached post, but your answer here with the code made it more understandable, at least for me. I've tried it and works pretty well. \$\endgroup\$
    – Hector
    Commented Sep 9, 2019 at 17:04
  • \$\begingroup\$ Hi, I've noticed that despite the blend there is still lurch when flying completely perpendicular to the plane. I've tried adjusting for Delta time and some other adjustments without success so far. \$\endgroup\$
    – Hector
    Commented Sep 10, 2019 at 12:36
  • \$\begingroup\$ You caught the edit I made to fix the order of the terms inside the lerp? \$\endgroup\$
    – DMGregory
    Commented Sep 10, 2019 at 12:43
  • \$\begingroup\$ No I didn't. Thanks, that fixed it! \$\endgroup\$
    – Hector
    Commented Sep 10, 2019 at 15:23
  • \$\begingroup\$ Hi, I wanted to ask you if It would be also necessary to do this if I want to use Quaternion.LookRotation with the NPC. \$\endgroup\$
    – Hector
    Commented Sep 30, 2019 at 16:57

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .