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\$
  • \$\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 Jul 1 '15 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. Jul 1 '15 at 2:09
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\$
  • \$\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 '15 at 2:44
  • \$\begingroup\$ @DMGregory What would you recommend to prevent a gimbal lock? \$\endgroup\$ – aggsol Jul 1 '15 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 '15 at 12:07
0
\$\begingroup\$

You can also lock z-rotation using:

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.