Camera/CameraMan.cs
using System;
using Clover;
using Clover.Player;
using Clover.Utilities;
using Sandbox;
[Category( "Clover/Camera" )]
[Icon( "camera" )]
public sealed class CameraMan : Component
{
public static CameraMan Instance { get; private set; }
[Property] public GameObject CameraPrefab { get; set; }
public HashSet<GameObject> Targets { get; private set; } = new();
private IEnumerable<CameraNode> _cameraNodes => Scene.GetAllComponents<CameraNode>().Where( x => !x.IsProxy );
private CameraNode _currentCameraNode;
public CameraNode MainCameraNode => _cameraNodes.OrderBy( x => x.Priority ).Reverse().FirstOrDefault();
private Vector3 _positionLerp;
private Rotation _rotationLerp;
private float _fovLerp;
public const float LerpSpeed = 5;
public const float DecaySpeed = 2.5f;
private CameraComponent CameraComponent;
protected override void OnAwake()
{
base.OnAwake();
// if ( IsProxy ) return;
Instance = this;
}
protected override void OnDestroy()
{
base.OnDestroy();
Instance = null;
}
protected override void OnStart()
{
base.OnStart();
if ( !CameraPrefab.IsValid() ) throw new Exception( "CameraPrefab is not valid" );
var camera = CameraPrefab.Clone();
camera.NetworkMode = NetworkMode.Never;
CameraComponent = camera.GetComponent<CameraComponent>();
camera.BreakFromPrefab();
if ( MainCameraNode.IsValid() )
{
_positionLerp = MainCameraNode.WorldPosition;
_rotationLerp = MainCameraNode.WorldRotation;
_fovLerp = MainCameraNode.FieldOfView;
}
}
protected override void OnUpdate()
{
var mainCameraNode = MainCameraNode;
if ( !mainCameraNode.IsValid() ) return;
if ( !CameraComponent.IsValid() ) return;
if ( !_currentCameraNode.IsValid() )
{
_currentCameraNode = mainCameraNode;
_currentCameraNode.OnActivated();
}
// when the camera changes
if ( _currentCameraNode.IsValid() && _currentCameraNode != mainCameraNode )
{
/*_positionLerp = mainCameraNode.WorldPosition;
_rotationLerp = mainCameraNode.WorldRotation;
_fovLerp = mainCameraNode.FieldOfView;
_previousCameraNode = mainCameraNode;*/
_currentCameraNode.OnDeactivated();
mainCameraNode.OnActivated();
_currentCameraNode = mainCameraNode;
if ( mainCameraNode.ShouldSnapInstantly )
{
_positionLerp = mainCameraNode.WorldPosition;
_rotationLerp = mainCameraNode.WorldRotation;
_fovLerp = mainCameraNode.FieldOfView;
}
}
Rotation wishedRot;
Vector3 wishedPos;
var _lerpSpeed = LerpSpeed;
var _decaySpeed = DecaySpeed;
wishedPos = mainCameraNode.WorldPosition;
if ( Targets.Count > 1 && mainCameraNode.FollowTargets )
{
var midpoint = GetTargetsMidpoint();
wishedRot = Rotation.LookAt( midpoint - mainCameraNode.WorldPosition, Vector3.Up );
}
else
{
wishedRot = mainCameraNode.WorldRotation;
// offset the camera when the player is moving to keep the player in the center of the screen
if ( !mainCameraNode.DollyNode.IsValid() && !mainCameraNode.Static && PlayerCharacter.Local.IsValid() )
{
wishedPos += PlayerCharacter.Local.CharacterController.Velocity * 0.4f;
if ( PlayerCharacter.Local.PlayerController.WishVelocity.Length == 0 )
{
_lerpSpeed /= 2;
}
}
}
// idle mode
if ( MainUi.Instance.IsValid() && MainUi.Instance.LastInput > MainUi.HideUiDelay * 3 )
{
var p = Sandbox.Utility.Noise.Perlin( Time.Now * 5f, Time.Now * 7f, Time.Now * 9f ) - 0.5f;
wishedRot *= Rotation.From( p * 2, p * 3, p );
_lerpSpeed = 0.1f;
_decaySpeed = 0.2f;
}
_positionLerp = _positionLerp.ExpDecayTo( wishedPos, _decaySpeed );
_rotationLerp = Rotation.Slerp( _rotationLerp, wishedRot, Time.Delta * _lerpSpeed );
_fovLerp = _fovLerp.LerpTo( mainCameraNode.FieldOfView, Time.Delta * _lerpSpeed );
if ( mainCameraNode.Lerping )
{
CameraComponent.WorldPosition = _positionLerp;
CameraComponent.WorldRotation = _rotationLerp;
}
else
{
CameraComponent.WorldPosition = wishedPos;
CameraComponent.WorldRotation = wishedRot;
}
CameraComponent.FieldOfView = _fovLerp;
}
public Vector3 GetTargetsMidpoint()
{
if ( Targets.Count == 0 ) return Vector3.Zero;
var midpoint = Vector3.Zero;
foreach ( var target in Targets.ToList() )
{
if ( !target.IsValid() )
{
RemoveTarget( target );
continue;
}
midpoint += target.WorldPosition;
}
return midpoint / Targets.Count;
}
public void SnapTo( Vector3 transformPosition, Rotation transformRotation )
{
_positionLerp = transformPosition;
_rotationLerp = transformRotation;
}
public void AddTarget( GameObject gameObject )
{
Targets.Add( gameObject );
}
public void RemoveTarget( GameObject gameObject )
{
Targets.Remove( gameObject );
}
}