Files
Cielonos/Assets/Scripts/MainGame/Effects/Feedbacks/Shakers/Cinemachine/CinemachinePositionShaker.cs
2026-04-18 13:57:19 -04:00

154 lines
4.7 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 System.Collections.Generic;
using SLSUtilities.Feedback;
using Unity.Cinemachine;
using UnityEngine;
namespace Cielonos.MainGame.Effects.Feedback
{
/// <summary>
/// 单个位移震动实例的运行时状态。
/// </summary>
public class CameraPositionShakeInstance : ShakeInstanceBase
{
public FloatCurveChannel intensityCurve;
public Vector3 amplitude;
public CameraPositionShakeInstance(
FeedbackTimeSettings timeSettings,
IFeedbackTimeProvider timeProvider,
FloatCurveChannel intensityCurve,
Vector3 amplitude,
float duration)
: base(timeSettings, timeProvider, duration)
{
this.intensityCurve = intensityCurve;
this.amplitude = amplitude;
}
/// <summary>
/// 计算当前帧的偏移量。
/// </summary>
public Vector3 Evaluate()
{
float normalizedTime = duration > 0 ? timer / duration : 1f;
float curveValue = intensityCurve.Evaluate(normalizedTime);
return amplitude * curveValue;
}
}
/// <summary>
/// 摄像机位移震动事件,用于解耦 Action 与 Shaker。
/// </summary>
public struct CameraPositionShakeEvent
{
private static event ShakeDelegate OnEvent;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void RuntimeInitialization() { OnEvent = null; }
public delegate void ShakeDelegate(
FeedbackContext feedbackContext,
FloatCurveChannel intensityCurve,
Vector3 amplitude,
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 intensityCurve,
Vector3 amplitude,
bool stop = false)
{
OnEvent?.Invoke(feedbackContext, intensityCurve, amplitude, stop);
}
}
/// <summary>
/// Cinemachine 位移震动器。挂载于 CinemachineCamera 上,
/// 监听 CameraPositionShakeEvent驱动 CinemachineCameraOffset 实现叠加震动。
/// </summary>
[AddComponentMenu("Cielonos/Feedback Shakers/Camera Position Shaker")]
[RequireComponent(typeof(CinemachineCamera))]
[RequireComponent(typeof(CinemachineCameraOffset))]
public class CinemachinePositionShaker : MonoBehaviour
{
private CinemachineCameraOffset _offsetComponent;
private Vector3 _initialOffset;
private readonly List<CameraPositionShakeInstance> _activeShakes = new List<CameraPositionShakeInstance>();
private void Awake()
{
_offsetComponent = GetComponent<CinemachineCameraOffset>();
_initialOffset = _offsetComponent.Offset;
}
private void OnEnable()
{
CameraPositionShakeEvent.Register(OnShakeEvent);
}
private void OnDisable()
{
CameraPositionShakeEvent.Unregister(OnShakeEvent);
StopAll();
}
private void Update()
{
if (_offsetComponent == null) return;
if (_activeShakes.Count == 0)
{
_offsetComponent.Offset = _initialOffset;
return;
}
Vector3 totalOffset = Vector3.zero;
for (int i = _activeShakes.Count - 1; i >= 0; i--)
{
CameraPositionShakeInstance shake = _activeShakes[i];
shake.Tick();
totalOffset += shake.Evaluate();
if (shake.IsFinished)
{
_activeShakes.RemoveAt(i);
}
}
_offsetComponent.Offset = _initialOffset + totalOffset;
}
private void OnShakeEvent(
FeedbackContext feedbackContext,
FloatCurveChannel intensityCurve,
Vector3 amplitude,
bool stop)
{
if (stop) { StopAll(); return; }
var instance = new CameraPositionShakeInstance(
feedbackContext.timeSettings,
feedbackContext.player.TimeProvider,
intensityCurve,
amplitude,
feedbackContext.duration
);
_activeShakes.Add(instance);
}
private void StopAll()
{
_activeShakes.Clear();
if (_offsetComponent != null)
{
_offsetComponent.Offset = _initialOffset;
}
}
}
}