vampire-like/Assets/Plugins/CameraModule/Runtime/CinemachineCameraController.cs

218 lines
7.0 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Cinemachine;
using UnityEngine;
namespace SepCore.CameraModule
{
/// <summary>
/// 基于 Cinemachine 的相机控制器。挂载到场景中的主相机上(主相机需有 CinemachineBrain
/// 会在主相机同级自动创建 CinemachineVirtualCamera避免虚拟相机成为被 Brain 驱动相机的子物体。
/// </summary>
[RequireComponent(typeof(CinemachineBrain))]
public class CinemachineCameraController : MonoBehaviour, ICameraController
{
[SerializeField] private CameraEffectSettings _settings = CameraEffectSettings.Default;
[SerializeField] private Vector3 _baseFollowOffset = new Vector3(0f, 10f, -10f);
[SerializeField] private bool _lockRotation = true;
[SerializeField] private Vector3 _fixedRotationEuler = new Vector3(90f, 0f, 0f);
private CinemachineVirtualCamera _virtualCamera;
private CinemachineTransposer _transposer;
private bool _followDampingEnabled = true;
private bool _movementDirectionOffsetEnabled = true;
private Vector3 _currentAppliedOffset;
private Vector3 _previousTargetPosition;
private bool _hasTarget;
public Transform Target { get; private set; }
public CameraEffectSettings CurrentSettings => _settings;
private void Awake()
{
CreateVirtualCamera();
}
private void OnValidate()
{
ApplyRotationMode();
}
private void LateUpdate()
{
if (!_hasTarget || Target == null)
return;
if (_movementDirectionOffsetEnabled)
UpdateMovementDirectionOffset();
else
ResetOffsetSmoothly();
_previousTargetPosition = Target.position;
if (_lockRotation)
ApplyFixedRotation();
}
public void SetTarget(Transform target)
{
Target = target;
_hasTarget = target != null;
if (_virtualCamera == null)
return;
_virtualCamera.Follow = target;
ApplyRotationMode();
if (_hasTarget)
{
_previousTargetPosition = target.position;
_currentAppliedOffset = _baseFollowOffset;
_transposer.m_FollowOffset = _baseFollowOffset;
}
}
public void EnableEffect(CameraEffectType effect)
{
switch (effect)
{
case CameraEffectType.FollowDamping:
_followDampingEnabled = true;
ApplyDampingValues();
break;
case CameraEffectType.MovementDirectionOffset:
_movementDirectionOffsetEnabled = true;
break;
}
}
public void DisableEffect(CameraEffectType effect)
{
switch (effect)
{
case CameraEffectType.FollowDamping:
_followDampingEnabled = false;
ApplyDampingValues();
break;
case CameraEffectType.MovementDirectionOffset:
_movementDirectionOffsetEnabled = false;
break;
}
}
public bool IsEffectEnabled(CameraEffectType effect)
{
return effect switch
{
CameraEffectType.FollowDamping => _followDampingEnabled,
CameraEffectType.MovementDirectionOffset => _movementDirectionOffsetEnabled,
_ => false,
};
}
public void ApplySettings(CameraEffectSettings settings)
{
_settings = settings;
ApplyDampingValues();
}
private void CreateVirtualCamera()
{
var go = new GameObject("CM_CameraController_VCam");
go.transform.SetParent(transform.parent, false);
go.transform.rotation = Quaternion.Euler(_fixedRotationEuler);
_virtualCamera = go.AddComponent<CinemachineVirtualCamera>();
_virtualCamera.m_Lens.FieldOfView = 60f;
_transposer = _virtualCamera.AddCinemachineComponent<CinemachineTransposer>();
_transposer.m_BindingMode = CinemachineTransposer.BindingMode.WorldSpace;
_transposer.m_FollowOffset = _baseFollowOffset;
ApplyRotationMode();
ApplyDampingValues();
}
private void ApplyRotationMode()
{
if (_virtualCamera == null)
return;
if (_lockRotation)
{
_virtualCamera.LookAt = null;
_virtualCamera.DestroyCinemachineComponent<CinemachineHardLookAt>();
ApplyFixedRotation();
return;
}
_virtualCamera.LookAt = Target;
if (_virtualCamera.GetCinemachineComponent<CinemachineHardLookAt>() == null)
_virtualCamera.AddCinemachineComponent<CinemachineHardLookAt>();
}
private void ApplyFixedRotation()
{
if (_virtualCamera == null)
return;
_virtualCamera.transform.rotation = Quaternion.Euler(_fixedRotationEuler);
}
private void ApplyDampingValues()
{
if (_transposer == null)
return;
if (_followDampingEnabled)
{
_transposer.m_XDamping = _settings.dampingX;
_transposer.m_YDamping = _settings.dampingY;
_transposer.m_ZDamping = _settings.dampingZ;
}
else
{
_transposer.m_XDamping = 0f;
_transposer.m_YDamping = 0f;
_transposer.m_ZDamping = 0f;
}
}
private void UpdateMovementDirectionOffset()
{
float dt = Time.deltaTime;
Vector3 velocity = (Target.position - _previousTargetPosition) / Mathf.Max(dt, 0.0001f);
float speed = new Vector3(velocity.x, 0f, velocity.z).magnitude;
Vector3 targetOffset = _baseFollowOffset;
if (speed > _settings.offsetDeadZone)
{
Vector3 dir = new Vector3(velocity.x, 0f, velocity.z).normalized;
float offsetAmount = Mathf.Min(speed * _settings.offsetStrength, _settings.maxOffsetDistance);
targetOffset += dir * offsetAmount;
}
_currentAppliedOffset = Vector3.Lerp(
_currentAppliedOffset,
targetOffset,
_settings.offsetSmoothing * dt
);
_transposer.m_FollowOffset = _currentAppliedOffset;
}
private void ResetOffsetSmoothly()
{
_currentAppliedOffset = Vector3.Lerp(
_currentAppliedOffset,
_baseFollowOffset,
_settings.offsetSmoothing * Time.deltaTime
);
_transposer.m_FollowOffset = _currentAppliedOffset;
}
}
}