I am trying to figure out how to solve this issue. My navball is tracking the ship's rotation incorrectly as shown in the image. When the ship rolls (Z Axis) to the right, the navball rotates down on the Y axis instead of on the Z axis like the ship.

How do I adjust the axis rotation tracking of the navball to track the respective axis of the ship? I feel like the answer has to be relatively simple. Just can't find hints to a solution.

Here is the code I am current using for the navball rotation;

 void FixedUpdate() {
   landerRot = lander.transform.localRotation;
   nb.localRotation = Quaternion.Slerp(transform.localRotation, landerRot, 1f);

Axis tracking issue

@DMGregory - Steps to reproduce

  • Add a cube and call it lander
  • Make the default main camera a child of the cube so that the camera is movement/rotation sync'd with the cube.
  • Create an empty game object called 'HudBase'
  • Add a child canvas to HudBase and set it to Screen Space - Camera
  • Add an empty gameobject to canvas called 'NavballSystem'
  • Add a sphere to the NavballSystem gameobject.
  • Create a script called 'hudNavball' and add the code snippet to it
  • Add the script to the `NavballSystem gameobject.
  • Use a crosshatched material on the navball to give visual of rotational movement.

Find a means of rotating the cube via script and observe the respective rotation of the navball.

  • \$\begingroup\$ What should slerp do? \$\endgroup\$
    – Zibelas
    Commented May 3 at 19:19
  • \$\begingroup\$ TBH...I don't quite know. I'm guessing it takes one rotation and compares it to the other? I just found that code from examples on google. \$\endgroup\$
    – Skittles
    Commented May 3 at 19:21
  • \$\begingroup\$ I have tried just assigning the landerRot directly to the nb.localRotation...but it still tracks the same axis'. \$\endgroup\$
    – Skittles
    Commented May 3 at 19:24
  • 1
    \$\begingroup\$ The way you've written it here, Quaternion.Slerp does nothing. You're taking a spherical linear interpolation that's 0% transform.localRotation and 100% landerRot — i.e. basically the same as nb.localRotation = landerRot. Are either the navball or the lander inside a parent that is itself rotated, or are they being rendered by different cameras? Try to walk us through the steps we'd need to follow to build a copy of the problematic parts of this scene in a new, empty, project. Once we can reproduce what you're seeing, we can test potential fixes to be sure they'll work for you. \$\endgroup\$
    – DMGregory
    Commented May 3 at 19:25
  • 1
    \$\begingroup\$ @DMGregory - I added the steps to reproduce in an update to the question above. \$\endgroup\$
    – Skittles
    Commented May 3 at 19:44

2 Answers 2


Disclaimer: I use Godot instead of Unity, so you may have to do some translation.

Rotation Order

In Godot it's possible to choose the order in which rotations are applied - the default is YXZ - this makes sense for first person shooters since the player probably rotates left and right (Y Axis) looks up and down (X axis) and then peeks/tilts the to peek round corners. I am going to stick with this rotation order since once your ship is landed on a planet we can effectively assume a flat world.

I will do a bit of foreshadowing and assume that:

Your ship has fallen over (so the top is pointing North), hence your nav ball is showing North with the blue side up.

Next we need to define two sets of axes - one for the ship/world and one for nav ball.


The main issue with the ship is that "UP" is defined as away from the planet, hence UP will change based on the position of the ship on the planet. Additionally either North or South is defined as a tangent line to the surface of the planet pointing at the pole - East and West are simply normal to both Up and North.

That said we are probably only going to deal with a small part of the planet so we can make some assumptions, within our active area, that:

  • The world is flat.
  • Up is axis aligned to positive Y (Gravity is negative Y)
  • We can define that East is positive X.
  • Hence positive Z is South.

If we start with the ship "fallen over" facing North. The priority rotation is around the Y Axis, hence whatever we do with pitch and roll the bearing will be correct.

Note: The ship needs to be facing North so that second axis (X) gets applied second, so that that pitch has priority over roll.

Nav Ball

We should lock the Nav Ball to the screen with the axes defined as:

  • Positive X - right of screen.
  • Positive Y - top of screen
  • Positive Z - towards the user.

However we have a problem because, whatever position the Nav/ship is in, we want the Nav Ball to rotate on the Z axis whenever the ship rolls. Further when the pitch changes we need that to have priority over the bearing.

I will cut to the chase and say (only) for the Nav Ball, we need the reverse rotation order, specifically: ZXY

The other thing to note is that rotations about the X and Y axes are now correct, however for display purposes we want a roll of the actual ship to be reversed on the Nav ball, so:

ship.rotation.x =  nav.rotation.x   
ship.rotation.y =  nav.rotation.y   
ship.rotation.z = -nav.rotation.z 

Note: The - sign applied to the Z rotation.


In my testing I apply the rotations to the Nav Ball, since each of potential torques that can be applied are aligned with the ship - however the Nav ball is already aligned, i.e.:

if Input.is_action_pressed("ui_left"):
    nav.global_rotate(Vector3.UP, delta * 5.0)

Then I use the code above to transfer the current orientation from the nav ball to the ship.

Answer / How to fix

As outlined above your lander is probably modeled with the top of the lander facing up if so you need to:

  • Rotate the lander mesh so it's pointing north.
  • Reverse the rotation order for the nav ball (ZXY).
  • Invert the rotation around the Z axis on the nav ball.

To change the mesh you can either do this in blender or just add an additional node in your scene tree so that you have two rotations, the higher level one will be the one thats rotated by the game - the lower level node will "fix" your mesh so that it points north.

  • \$\begingroup\$ This answer does not make sense for Unity. You've described a right-handed coordinate scheme while Unity's is left-handed. Unity's rotation order when using Euler/Tait-Bryan angles is always ZXY (from "most local" to "most global" extrinsic rotations), but the code in the question does not use angles at all, and copies the orientation via quaternion, so there's no order of application — the rotation happens "all at once" not axis-by-axis sequentially. \$\endgroup\$
    – DMGregory
    Commented May 4 at 10:56
  • \$\begingroup\$ docs.godotengine.org/en/stable/_images/… You are correct Godot is RH and Unity LH - I don't think that is a major issue as it will just reverse two of the rotation angles. If Unity doesn't have the ability to change the order of the rotation I will have to figure out how to do the same manipulation with quaternions. That may take me some time (not my strongest point) however I have this working as described in Godot. \$\endgroup\$
    – DavidT
    Commented May 4 at 14:19

If you have two nodes, each of which are rotated with a quaternion. When you copy the quaternion value from one node to the other, the rotations within their own local axes will be the same. Hence as long as you rotate the parents of these nodes to setup their neutral position (how you need them), the rotations should work fine.

That said, I did some thinking to figure out what the effect would be of swapping the order of the axes and/or negating some of them.

There are 6 possible ordering of three axes (XYZ, YZX, ZXY, ZYX, YXZ, XZY) and each of the axes could be negated (8 permutations). I am not considering the real part (rotation angle) since negating all 4 values has no effect, so any change to the sign of the real part could be cancelled out by negating all four. Hence there are 6 * 8 = 48 total permutations.

Of these 24 preserve the chirality and 24 swap the chirality. If the chirality is presevered, it is possible to rotate the axes to create the same effect. If the chirality is swapped (LH to RH or vice versa) no such rotation exists.

Given that, I was able to create the following table of all permutations that do not change the chirality:

( X,  Y,  Z) -- []

Axis 180 -- Negate two Axes
( X, -Y, -Z) -- [X180]
(-X,  Y, -Z) -- [Y180]
(-X, -Y,  Z) -- [Z180]

Axis 90 -- Swap two Axes and negate one that you swapped
( X, -Z,  Y) -- [X+90]
( X,  Z, -Y) -- [X-90]
( Z,  Y, -X) -- [Y+90]
(-Z,  Y,  X) -- [Y-90]
(-Y,  X,  Z) -- [Z+90]
( Y, -X,  Z) -- [Z-90]

Two 90 rotations -- Rotate X,Y & Z, then optionally negate two Axes
( Z,  X,  Y) -- [Y+90, X+90]
(-Z, -X,  Y) -- [Y-90, X+90]
(-Z,  X, -Y) -- [Y-90, X-90]
( Z, -X, -Y) -- [Y+90, X-90]

( Y,  Z,  X) -- [Y-90, Z-90]
(-Y, -Z,  X) -- [Y-90, Z+90]
(-Y,  Z, -X) -- [Y+90, Z+90]
( Y, -Z, -X) -- [Y+90, Z-90]

180 + 90 -- Swap two axes and then negate the other axis or all three axes.
(-X,  Z,  Y) -- [Y180, X+90]
(-X, -Z, -Y) -- [Y180, X-90]

( Z, -Y,  X) -- [Y+90, X180]
(-Z, -Y, -X) -- [Y-90, X180]

( Y,  X, -Z) -- [Y180, Z-90]
(-Y, -X, -Z) -- [Y180, Z+90]

As explained in my Euler angle based answer, the ship and nav ball have different chiralities, because it is necessary to negate the roll (Z) axis - and negating only one axis results in a change of chirality.

If the ships neutral position is flying north, then the ships roll is aligned with both the world Z and the screen space Z so all that is needed (in either Godot or Unity) is to flip the Z axis.

If the ships neutral position is pointing up (+Y), the nav ball, will still be aligned with the Z axis (+Z in Godot, -Z in Unity) however the navball will now be showing "all blue" as that is the correct indicator for a shipping flying up. Hence a change in axes is required, equivalent to a 90 rotation around the X axis:

  • ( X, -Z, Y) -- [X+90] - In Godot
  • ( X, Z, -Y) -- [X-90] - In Unity

Finally we need to flip the Z axis as before leaving us with:

  • ( X, Z, Y) - In Godot
  • ( X, -Z, -Y) - In Unity

Edit: These three lines above incorrect:

  • ( X, -Z, Y) -- [X+90] - In Godot
  • ( X, Z, -Y) -- [X-90] - In Unity

Finally we need to flip the Z axis as before leaving us with:


  • If the ship was pointing up the roll axis would be the Y axis.
  • We need to flip the Y axis Before we rotate the axes.
  • The rotation is 180 degrees out, it should be:
    • Godot: X-90
    • Unity: X+90

However these three mistakes cancel each other out, hence the final answer is still:

  • ( X, Z, Y) - In Godot
  • ( X, -Z, -Y) - In Unity
  • \$\begingroup\$ Thanks, @DavidT! \$\endgroup\$
    – Skittles
    Commented May 9 at 21:09

You must log in to answer this question.

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