Files
Cielonos/Assets/Scripts/MainGame/Effects/Feedbacks/Shakers/Cinemachine/CameraFovShaker.cs
SoulliesOfficial 0902ca8a9e Bezi回来了
2026-04-28 15:46:32 -04:00

152 lines
4.5 KiB
C#
Raw 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 System.Collections.Generic;
using SLSUtilities.Feedback;
using Unity.Cinemachine;
using UnityEngine;
namespace Cielonos.MainGame.Effects.Feedback
{
/// <summary>
/// 单个FOV震动实例的运行时状态。
/// </summary>
public class CameraFovShakeInstance : ShakeInstanceBase
{
public FloatCurveChannel fovCurve;
public CameraFovShakeInstance(FeedbackTimeSettings timeSettings, IFeedbackTimeProvider timeProvider,
FloatCurveChannel fovCurve, float duration) : base(timeSettings, timeProvider, duration)
{
this.fovCurve = fovCurve;
}
}
/// <summary>
/// 摄像机视野角(FOV)震动事件,用于解耦 Action 与 Shaker。
/// </summary>
public struct CameraFovShakeEvent
{
private static event ShakeDelegate OnEvent;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void RuntimeInitialization() { OnEvent = null; }
public delegate void ShakeDelegate(
FeedbackContext feedbackContext,
FloatCurveChannel fovCurve,
bool stop
);
public static void Register(ShakeDelegate callback) { OnEvent += callback; }
public static void Unregister(ShakeDelegate callback) { OnEvent -= callback; }
public static void Trigger(
FeedbackContext feedbackContext,
FloatCurveChannel fovCurve,
bool stop = false)
{
OnEvent?.Invoke(feedbackContext, fovCurve, stop);
}
}
/// <summary>
/// CinemachineCamera FOV 震动聚合器。挂载于 CinemachineCamera 上,
/// 监听 CameraFovShakeEvent叠加驱动 Lens.FieldOfView。
/// </summary>
[AddComponentMenu("Cielonos/Feedback Shakers/Camera FOV Shaker")]
[RequireComponent(typeof(CinemachineCamera))]
public class CameraFovShaker : MonoBehaviour
{
private CinemachineCamera _camera;
public float initialFov;
private readonly List<CameraFovShakeInstance> _activeShakes = new List<CameraFovShakeInstance>();
private void Awake()
{
_camera = GetComponent<CinemachineCamera>();
initialFov = _camera.Lens.FieldOfView;
}
private void OnEnable()
{
CameraFovShakeEvent.Register(OnShakeEvent);
}
private void OnDisable()
{
CameraFovShakeEvent.Unregister(OnShakeEvent);
StopAll();
}
private void Update()
{
if (_camera == null) return;
if (_activeShakes.Count == 0)
{
SetFov(initialFov);
return;
}
float additiveFov = 0f;
float absoluteFov = 0f;
bool hasAbsolute = false;
for (int i = _activeShakes.Count - 1; i >= 0; i--)
{
CameraFovShakeInstance shake = _activeShakes[i];
shake.Tick();
float normalizedTime = shake.timer / shake.duration;
if (shake.fovCurve.relativeToInitial)
{
additiveFov += shake.fovCurve.Evaluate(normalizedTime);
}
else
{
absoluteFov = shake.fovCurve.Evaluate(normalizedTime);
hasAbsolute = true;
}
if (shake.IsFinished)
{
_activeShakes.RemoveAt(i);
}
}
float finalFov = hasAbsolute ? absoluteFov : initialFov + additiveFov;
SetFov(finalFov);
}
private void OnShakeEvent(
FeedbackContext feedbackContext,
FloatCurveChannel fovCurve,
bool stop)
{
if (stop) { StopAll(); return; }
var instance = new CameraFovShakeInstance(
feedbackContext.timeSettings,
feedbackContext.player.TimeProvider,
fovCurve,
feedbackContext.duration
);
_activeShakes.Add(instance);
}
private void SetFov(float fov)
{
LensSettings lens = _camera.Lens;
lens.FieldOfView = fov;
_camera.Lens = lens;
}
private void StopAll()
{
_activeShakes.Clear();
if (_camera != null)
{
SetFov(initialFov);
}
}
}
}