First-person controller in Unity Part 1 - camera control

Create a first-person camera controller in Unity from scratch

First-person controller in Unity Part 1 - camera control

This short tutorial series will walk you through creating a fully functioning FPS controller in Unity.

Let's start with the camera controller. The vertical mouse movement will rotate the camera up and down. The horizontal mouse movement will rotate the whole player left and right (we are rotating the whole body, not just the "head").

Setup and horizontal camera movement

Make a new script called CameraController and attach it to the Player (not the Main Camera!). We are going to store mouse input in a Vector2 every frame, then use the x component to rotate the Player body.

public class CameraController : MonoBehaviour {
    void Update() {
        Vector2 mouseInput = new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"));
        transform.Rotate(Vector3.up * mouseInput.x);
    }
}

We can control the sensitivity of the mouse movement by multiplying the rotation. There is no need to access sensitivity from another class so we won't make it public. Unity only shows public variables in the inspector by default. We can change this behavior by adding the [SerializeField] attribute.
Don't forget to input a value in the inspector, otherwise it will be 0 by default and the camera won't move at all!

[SerializeField] float sensitivity;
Add this field to the CameraController class
/// Multiply the rotation amount with the sensitivity
transform.Rotate(Vector3.up * mouseInput.x * sensitivity);
Change this line in the Update() method

Vertical camera movement

The next step is the vertical movement. In this case, we won't be rotating the whole body, just the camera so we need a reference to the Main Camera's Transform component.

[SerializeField] Transform cameraTransform;
Add this field to the CameraController class

Back in the editor, drag the Main Camera from the hierarchy to the Camera Transform field in the Player's Inspector. Now we can access our camera in the code.

We are going to store the angle of our camera in a variable called pitch, then update it with the vertical mouse movement (mouseInput.y) every frame.

float pitch = 0;
Add this field to the CameraController class
// Don't forget to multiply with the sensitivity!
pitch += mouseInput.y * sensitivity; 
// Update the local rotation of the camera
cameraTransform.localEulerAngles = new Vector3(pitch, 0, 0);
Add these lines to the Update() method

Something is clearly wrong here. First, the camera can rotate infinitely. This is neither realistic nor convenient to control. Second, the mouse movement is inverted (when the mouse moves up, the player looks down).

A simple fix for the infinite rotation is to constrain the pitch between -90 and 90 degrees. The inverted control can be solved by subtracting from the pitch instead of adding to it.

pitch -= mouseInput.y * sensitivity;
pitch = Mathf.Clamp(pitch, -90f, 90f);
cameraTransform.localEulerAngles = new Vector3(pitch, 0, 0);

Hiding and locking the cursor

Let's lock the cursor to the game window and hide it so it doesn't interfere visually with the game. Setting Cursor.lockState to Locked will lock the cursor to the middle of the screen and hide it by default.

void Start() {
	Cursor.lockState = CursorLockMode.Locked;
}
We only need to set this once in the Start() method

Congratulations, now you have a working first-person camera controller! We are going to add the ability to control movement in Part 2 - movement.

The complete CameraController.cs code

using System;
using UnityEngine;

public class CameraController : MonoBehaviour {
    [SerializeField] Transform cameraTransform;
    [SerializeField] float sensitivity;

    float pitch = 0;

    void Start() {
        Cursor.lockState = CursorLockMode.Locked;
    }

    void Update() {
        Vector2 mouseInput = new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"));
        transform.Rotate(Vector3.up * mouseInput.x * sensitivity);

        pitch -= mouseInput.y * sensitivity;
        pitch = Mathf.Clamp(pitch, -90f, 90f);
        cameraTransform.localEulerAngles = new Vector3(pitch, 0, 0);
    }
}

If you have any questions or have found any errors in the guide, please send an e-mail to contact@gamedevbrew.com