4
\$\begingroup\$

My goal is to program a camera to point towards the mouse cursor. I attached the following script to the Main Camera.

using UnityEngine;
using System.Collections;

public class CameraController : MonoBehaviour {

    public float sensitivity;

    // Update is called once per frame
    void Update () {
        transform.Rotate (Input.GetAxis ("Mouse Y") * -1 * sensitivity, Input.GetAxis ("Mouse X") * sensitivity, 0);
    }
}

I "told" the camera to rotate around the x and y axes. Whenever it does this, the camera tilts sideways. When I checked the rotation of Main Camera, I saw that the z rotation had changed. Why is it rotating around the z axis when I left it at 0?

\$\endgroup\$
3
  • \$\begingroup\$ To clarify: are you observing that your rendered view is rolling, so that it's looking at the world sideways? Or is the rendered view as-desired, and you're just wondering why the inspector reports a non-zero z rotation value? \$\endgroup\$
    – DMGregory
    Commented Jul 1, 2015 at 2:01
  • \$\begingroup\$ @DMGregory My rendered view is actually tilting sideways. I edited my post to reflect that. Thanks for letting me know! \$\endgroup\$
    – Nick B.
    Commented Jul 1, 2015 at 2:09

2 Answers 2

5
\$\begingroup\$

Rotations are extremely order-dependent. Doubly so when you're composing rotations in local space (so the axes you're rotating around are themselves rotating from one frame to the next)

As an extreme example, imagine that you start facing z+, and in one frame you pitch (x rotation) 90 degrees up.

In the next frame, you yaw (y rotation) 90 degrees left. Since you're already looking straight up, your local y axis is pointing "back" along the world z- axis, so this leaves you looking along the x- axis, with your local y axis still pointing "back" along world z-.

This is the same as yawing 90 degrees left and then rolling (z rotation) 90 degrees left.

So: composing local x & y rotations, compounded from frame to frame, can turn into z rotations.

One way to fix this is to always pitch along your local x axis, but always yaw around the world y axis (if you always want the camera "up" vector facing up). eg:

void Update () {
    float pitch = Input.GetAxis ("Mouse Y") * -1f * sensitivity;
    transform.Rotate (pitch * Vector3.right, Space.Self); 

    float yaw = Input.GetAxis ("Mouse X") * sensitivity;
    transform.Rotate (yaw * Vector3.up, Space.World);
}

Note that this still has the problem of gimbal lock common to Euler angle / yaw-pitch-roll rotation systems, and isn't suitable if you want the player to be able to point their camera straight up or straight down.

\$\endgroup\$
8
  • \$\begingroup\$ Thanks, it worked! As a note for others who use this code, don't forget to declare sensitivity as a public float. I had to set mine to about 7.5 for it to behave correctly. \$\endgroup\$
    – Nick B.
    Commented Jul 1, 2015 at 2:44
  • \$\begingroup\$ @DMGregory What would you recommend to prevent a gimbal lock? \$\endgroup\$
    – aggsol
    Commented Jul 1, 2015 at 8:13
  • \$\begingroup\$ @Code it depends on the needs of the game. For instance, a flight sim can get away with a camera that can roll or go upside-down to sidestep gimbal lock, then gradually rights itself. That's less appropriate for a 1st or 3rd person action game, which may want to clamp the rotation somewhere. \$\endgroup\$
    – DMGregory
    Commented Jul 1, 2015 at 12:07
  • \$\begingroup\$ I am doing as you advised @DMGregory, but I still have the problem with rolling. So I changed the order of my rotations an translations in this way: m_ViewMatrix =inverse(glm::rotate(glm::mat4(1.0f), m_Rotation, camUp)) * glm::translate(glm::mat4(1.0f), m_Position)* inverse(glm::rotate(glm::mat4(1.0f), m_TiltRotation, camRight)) *m_ViewMatrix; m_Position = { 0.0f, 0.0f, 0.0f }; m_Rotation = 0.0f; m_TiltRotation = 0.0f; But I still get rolling around the z axis. So what is wrong with this? \$\endgroup\$ Commented Apr 13, 2022 at 15:15
  • \$\begingroup\$ @ŽarkoTomičić You're rotating about the camera's up vector instead of the world's. More details here. \$\endgroup\$
    – DMGregory
    Commented Apr 13, 2022 at 15:29
0
\$\begingroup\$

You can also lock z-rotation using:

float z=transform.eulerAngles.z;
transform.Rotate(0,0,-z);
\$\endgroup\$

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