Cinemachine

First-Person Controller

This example demonstrates how to implement a first-person controller in Unity using Cinemachine. It utilizes the Cinemachine Virtual Camera's 3rd Person Follow for a comprehensive experience. The script includes configurations for camera settings, mouse sensitivity, and managing player input for movement, rotation, crouching, and jumping.

Furthermore, the example seamlessly integrates events to handle camera transitions during crouch and uncrouch actions. Overall, it provides a solid foundation for a first-person perspective with Cinemachine integration, effectively managing both camera and player interactions.

In this setup, the Character will manage the yaw rotation (rotation along the character's up-axis), while the parented 'Camera Target' GameObject will handle the camera's pitch rotation.

public class FirstPersonController : MonoBehaviour
{
    ..

    /// <summary>
    /// Add input (affecting Yaw).
    /// This is applied to the Character's rotation.
    /// </summary>
    
    public void AddControlYawInput(float value)
    {
        _character.AddYawInput(value);
    }
    
    /// <summary>
    /// Add input (affecting Pitch).
    /// This is applied to the cameraTarget's local rotation.
    /// </summary>
    
    public void AddControlPitchInput(float value, float minValue = -80.0f, float maxValue = 80.0f)
    {
        if (value == 0.0f)
            return;
        
        _cameraTargetPitch = MathLib.ClampAngle(_cameraTargetPitch + value, minValue, maxValue);
        cameraTarget.transform.localRotation = Quaternion.Euler(-_cameraTargetPitch, 0.0f, 0.0f);
    }
}

It's important to disable the character's rotation mode since, in this example, we'll handle it ourselves.

private void Start()
{
    Cursor.lockState = CursorLockMode.Locked;
    
    // Disable Character's rotation mode, we'll handle it here
    
    _character.SetRotationMode(Character.RotationMode.None);
}

Additionally, we'll use the Character Crouched and UnCrouched events to trigger a crouch/uncrouch animation, relying on Cinemachine's transitions.

private void OnEnable()
{
    // Subscribe to Character events
    
    _character.Crouched += OnCrouched;
    _character.UnCrouched += OnUnCrouched;
}

private void OnDisable()
{
    // Unsubscribe to Character events
    
    _character.Crouched -= OnCrouched;
    _character.UnCrouched -= OnUnCrouched;
}

..

/// <summary>
/// When character crouches, toggle Crouched / UnCrouched cameras.
/// </summary>

private void OnCrouched()
{
    crouchedCamera.SetActive(true);
    unCrouchedCamera.SetActive(false);
}

/// <summary>
/// When character un-crouches, toggle Crouched / UnCrouched cameras.
/// </summary>

private void OnUnCrouched()
{
    crouchedCamera.SetActive(false);
    unCrouchedCamera.SetActive(true);
}

Finally, we handle player input:

private void Update()
{
    // Movement input
    
    Vector2 moveInput = new Vector2
    {
        x = Input.GetAxisRaw("Horizontal"),
        y = Input.GetAxisRaw("Vertical")
    };
    
    // Movement direction relative to Character's forward

    Vector3 movementDirection = Vector3.zero;

    movementDirection += _character.GetRightVector() * moveInput.x;
    movementDirection += _character.GetForwardVector() * moveInput.y;
    
    // Set Character movement direction

    _character.SetMovementDirection(movementDirection);
    
    // Look input

    Vector2 lookInput = new Vector2
    {
        x = Input.GetAxisRaw("Mouse X"),
        y = Input.GetAxisRaw("Mouse Y")
    };
    
    // Add yaw input, this update character's yaw rotation

    AddControlYawInput(lookInput.x * lookSensitivity.x);
    
    // Add pitch input (look up / look down), this update cameraTarget's local rotation
    
    AddControlPitchInput(lookInput.y * lookSensitivity.y, minPitch, maxPitch);
    
    // Crouch input

    if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.C))
        _character.Crouch();
    else if (Input.GetKeyUp(KeyCode.LeftControl) || Input.GetKeyUp(KeyCode.C))
        _character.UnCrouch();
    
    // Jump input

    if (Input.GetButtonDown("Jump"))
        _character.Jump();
    else if (Input.GetButtonUp("Jump"))
        _character.StopJumping();
}

Third-Person Controller

The following example illustrates the implementation of a third-person controller in Unity using Cinemachine. It leverages the Cinemachine Virtual Camera's 3rd Person Follow feature for a comprehensive experience. The controller encompasses configurations for camera settings, mouse sensitivity, and the management of player input for movement, rotation, crouching, and jumping.

Initially, we implement methods to control the camera's rotation and adjust its follow distance:

/// <summary>
/// Add input (affecting Yaw). 
/// This is applied to the followTarget's yaw rotation.
/// </summary>

public void AddControlYawInput(float value, float minValue = -180.0f, float maxValue = 180.0f)
{
    if (value != 0.0f) _cameraTargetYaw = MathLib.ClampAngle(_cameraTargetYaw + value, minValue, maxValue);
}

/// <summary>
/// Add input (affecting Pitch). 
/// This is applied to the followTarget's pitch rotation.
/// </summary>

public void AddControlPitchInput(float value, float minValue = -80.0f, float maxValue = 80.0f)
{
    if (value == 0.0f)
        return;
    
    if (invertLook)
        value = -value;
    
    _cameraTargetPitch = MathLib.ClampAngle(_cameraTargetPitch + value, minValue, maxValue);
}

/// <summary>
/// Adds input (affecting follow distance).
/// </summary>

public virtual void AddControlZoomInput(float value)
{
    followDistance = Mathf.Clamp(followDistance - value, followMinDistance, followMaxDistance);
}

Subsequently, we adjust both the rotation of the followTarget GameObject and the CameraDistance of the Cinemachine virtual camera. This essentially means that we are exerting control over the Cinemachine camera by manipulating the followTarget GameObject.

/// <summary>
/// Update followTarget rotation using _cameraTargetYaw and _cameraTargetPitch values and its follow distance.
/// </summary>

private void UpdateCamera()
{
    followTarget.transform.rotation = Quaternion.Euler(_cameraTargetPitch, _cameraTargetYaw, 0.0f);
    
    _cmThirdPersonFollow.CameraDistance = 
        Mathf.SmoothDamp(_cmThirdPersonFollow.CameraDistance, followDistance, ref _followDistanceSmoothVelocity, 0.1f);
}

private void LateUpdate()
{
    // Update cameraTarget rotation using our yaw and pitch values
    
    UpdateCamera();
}

Finally, we handle player input:

private void Update()
{
    // Movement input
    
    Vector2 inputMove = new Vector2()
    {
        x = Input.GetAxisRaw("Horizontal"),
        y = Input.GetAxisRaw("Vertical")
    };
    
    // Set Movement direction in world space
    
    Vector3 movementDirection =  Vector3.zero;

    movementDirection += Vector3.right * inputMove.x;
    movementDirection += Vector3.forward * inputMove.y;
    
    // If character has a camera assigned...
    
    if (_character.camera)
    {
        // Make movement direction relative to its camera view direction
        
        movementDirection = movementDirection.relativeTo(_character.cameraTransform);
    }
    
    // Set Character movement direction

    _character.SetMovementDirection(movementDirection);
    
    // Crouch input
    
    if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.C))
        _character.Crouch();
    else if (Input.GetKeyUp(KeyCode.LeftControl) || Input.GetKeyUp(KeyCode.C))
        _character.UnCrouch();
    
    // Jump input
    
    if (Input.GetButtonDown("Jump"))
        _character.Jump();
    else if (Input.GetButtonUp("Jump"))
        _character.StopJumping();
    
    // Look input

    Vector2 lookInput = new Vector2
    {
        x = Input.GetAxisRaw("Mouse X"),
        y = Input.GetAxisRaw("Mouse Y")
    };
    
    AddControlYawInput(lookInput.x * lookSensitivity.x);
    AddControlPitchInput(lookInput.y * lookSensitivity.y, minPitch, maxPitch);
    
    // Zoom (in / out) input

    float mouseScrollInput = Input.GetAxisRaw("Mouse ScrollWheel");
    AddControlZoomInput(mouseScrollInput);
}

Last updated