16
\$\begingroup\$

I want to make a first person camera that rotates with the mouse.

I looked at the Input.GetAxis Scripting API page and found a sample code, which I have included at the bottom of my post. Upon trying it out, I realized that although it has the same basic functionality I hoped it would have, it does not keep the camera parallel to the xz plane, particularly when moving the mouse in circles. After a while the camera would be at an odd angle, and the player would be completely discombobulated!

Is there a quick fix to this code that would restrict the camera movement somehow, or is there a better way to rotate the camera?

 using UnityEngine;
 using System.Collections;

     public class ExampleClass : MonoBehaviour {
         public float horizontalSpeed = 2.0F;
         public float verticalSpeed = 2.0F;
         void Update() {
             float h = horizontalSpeed * Input.GetAxis("Mouse X");
             float v = verticalSpeed * Input.GetAxis("Mouse Y");
             transform.Rotate(v, h, 0);
         }
     }
\$\endgroup\$
3
  • \$\begingroup\$ What you are experiencing sounds like gimbal lock. It happens when you are using euler angles for rotation. I'd have to do some testing to offer a solution. Basically, you need to use quaternions. They don't suffer from gimbal lock. \$\endgroup\$ Commented Jul 28, 2015 at 3:56
  • \$\begingroup\$ How would I use a quaternion to do rotation? I tried transform.rotation = Quaternion.Slerp(-Input.GetAxis("Mouse X"), Input.GetAxis("Mouse X"), Time.deltaTime * freeLookTurningSpeed); , but that gave me errors. \$\endgroup\$ Commented Jul 29, 2015 at 5:30
  • \$\begingroup\$ Ok, I made a test. Fortunately, it's not gimbal lock. I'll post the solution as an answer for you. \$\endgroup\$ Commented Jul 29, 2015 at 7:28

2 Answers 2

24
\$\begingroup\$

The problem is that you are updating the existing rotation instead of tracking it yourself and replacing it each update.

When you update the rotation it combines your new rotation with the old one which leads to unexpected behaviour.

The solution is to to keep track of your accumulated rotation and reset the rotation entirely each update with your new values.

Credit where due, I found the answer here: Unity Answers: how-to-lock-or-set-the-cameras-z-rotation-to-zero

Working Code:

using UnityEngine;
using System.Collections;

public class FirstPersonCam : MonoBehaviour {

    public float speedH = 2.0f;
    public float speedV = 2.0f;

    private float yaw = 0.0f;
    private float pitch = 0.0f;

    void Update () {
        yaw += speedH * Input.GetAxis("Mouse X");
        pitch -= speedV * Input.GetAxis("Mouse Y");

        transform.eulerAngles = new Vector3(pitch, yaw, 0.0f);
    }
}

Key differences:

  • Store the yaw and pitch as class members, rather than local method variables so you can keep track of the accumulated values.
  • Use += and/or -= to accumulate each update.
  • Use eulerAngles (to overwrite the rotation value each update) instead of Rotate (which applies your new rotation onto the old one).
\$\endgroup\$
1
  • \$\begingroup\$ Your welcome. If this solution I posted solved the original problem you were having, you should accept it as the answer. If you would like to get help with your new problem, you should post a new question to keep things tidy and more likely for others to be able to help you or find your question if they are having a similar problem. The comment areas are not meant for extended dialog. Cheers. \$\endgroup\$ Commented Jul 31, 2015 at 6:55
0
\$\begingroup\$

What if you make the camera child of an empty gameobject. Then with one script you manage the empty gameobject rotation relative to UP vector.(MouseX) With another script atached to the camera gameobject you manage rotation relative to Right vector (MouseY)

\$\endgroup\$

You must log in to answer this question.

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