3
\$\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

2 Answers 2

4
\$\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.
    Jul 1, 2015 at 2:44
  • \$\begingroup\$ @DMGregory What would you recommend to prevent a gimbal lock? \$\endgroup\$
    – aggsol
    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
    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\$ Apr 13 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
    Apr 13 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 .