ZoomComponent.cs

A component that manages camera zoom and switching between first and third person by adjusting camera FieldOfView and toggling a PlayerController. It reads input actions to zoom in/out in third person and to change view, clamps FOV, and sets the controller's ThirdPerson flag when switching.

Native Interop
using Sandbox;
using System;
using System.Linq;

/// <summary>
/// Zoom Component that uses FOV to switch between third and first person.
/// </summary>
[Title( "Simple Zoom Component" )]
public class ZoomComponent : Component
{
	[Property, ReadOnly] public EFovState FovState { get; set; } = EFovState.FirstPerson;
	[Property] public string ZoomInActionName { get; set; } = "Zoom In";
	[Property] public string ZoomOutActionName { get; set; } = "Zoom Out";
	[Property] public string ChangeViewActionName { get; set; } = "view";
	[Property] public float ZoomStep { get; set; } = 3.0f;
	[Property] public float MinFov { get; set; } = 30.0f;
	[Property] public float MaxFov { get; set; } = 120.0f;
	[Property, ReadOnly] private float _targetFovDiff = 0.0f;
	[Property, ReadOnly] private float _baseFov = 0.0f;
	[Property, ReadOnly] private bool _canZoom = true;
	private PlayerController _playerController = null;
	private CameraComponent _camera = null;

	public enum EFovState { FirstPerson, FirstToThird, ThirdPerson, ThirdToFirst };

	protected override void OnStart()
	{
		base.OnStart();

		_playerController ??= GetComponent<PlayerController>();
		_camera ??= Scene.GetAllComponents<CameraComponent>().FirstOrDefault(x => x.Enabled);
		if(_camera != null)
			_baseFov = _camera.FieldOfView;
	}

	protected override void OnUpdate()
	{
		HandleInput();
	}

	private void HandleInput()
	{
		if ( Input.Down( ChangeViewActionName ) )
		{
			FovState = _playerController.ThirdPerson ? EFovState.ThirdPerson : EFovState.FirstPerson;
		}

		if ( _canZoom )
		{
			switch ( FovState )
			{
				case EFovState.ThirdPerson:
					float input = 0f;

					if (Input.Down( ZoomInActionName ))  input -= ZoomStep;
					if (Input.Down( ZoomOutActionName )) input += ZoomStep;

					_targetFovDiff += input * ZoomStep;
					_targetFovDiff = Math.Clamp(_targetFovDiff, MinFov - _baseFov - 0.0001f, MaxFov - _baseFov);

					float targetFov = _baseFov + _targetFovDiff;

					if (targetFov < MinFov)
					{
						SetFovState(EFovState.ThirdToFirst);
					}

					_camera.FieldOfView = targetFov;

					break;
				case EFovState.FirstPerson:
					if ( Input.Down( ZoomOutActionName ) )
					{
						SetFovState( EFovState.FirstToThird );
					}
					break;
				case EFovState.FirstToThird:
					break;
				case EFovState.ThirdToFirst:
					break;
				default:
					throw new ArgumentOutOfRangeException( nameof(FovState), FovState, null );
			}
		}
	}

	private void SetFovState( EFovState fovState )
	{
		switch ( fovState )
		{
			case EFovState.FirstPerson:
				break;
			case EFovState.FirstToThird:
				_canZoom = false;
				_targetFovDiff = MinFov - _baseFov;
				_camera.FieldOfView = MinFov;
				_playerController.ThirdPerson = true;
				FovState = EFovState.ThirdPerson;

				break;
			case EFovState.ThirdPerson:
				break;
			case EFovState.ThirdToFirst:
				_canZoom = false;
				_targetFovDiff = 0.0f;
				_camera.FieldOfView = _baseFov;
				_playerController.ThirdPerson = false;
				FovState = EFovState.FirstPerson;

				break;
			default:
				throw new ArgumentOutOfRangeException( nameof(fovState), fovState, null );
		}

		_canZoom = true;
	}
}