# 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.

```csharp
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.

```csharp
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.

```csharp
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:

```csharp
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:

```csharp
/// <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`.

```csharp
/// <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:

```csharp
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);
}
```
