using System.Collections.Generic; using SLSUtilities.Feedback; using SLSUtilities.Rendering.PostProcessing; using UnityEngine; namespace Cielonos.MainGame.Effects.Feedback { /// /// 径向模糊震动事件。 /// public struct RadialBlurShakeEvent { private static event ShakeDelegate OnEvent; [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void RuntimeInitialization() { OnEvent = null; } public delegate void ShakeDelegate(FeedbackContext feedbackContext, FloatCurveChannel intensityCurve, bool modifyCenter, Vector2 center, 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, bool modifyCenter = false, Vector2 center = default, bool stop = false) { OnEvent?.Invoke(feedbackContext, intensityCurve, modifyCenter, center, stop); } } /// /// 径向模糊震动实例。 /// public class RadialBlurShakeInstance : ShakeInstanceBase { public readonly FloatCurveChannel intensityCurve; public readonly bool modifyCenter; public readonly Vector2 center; public RadialBlurShakeInstance( FeedbackContext feedbackContext, FloatCurveChannel intensityCurve, bool modifyCenter, Vector2 center) : base(feedbackContext.timeSettings, feedbackContext.player.TimeProvider, feedbackContext.duration) { this.intensityCurve = intensityCurve; this.modifyCenter = modifyCenter; this.center = center; } } /// /// RadialBlur 的震动聚合器。 /// [AddComponentMenu("SLS Utilities/Feedback Shakers/Radial Blur Shaker")] public class RadialBlurShaker : MonoBehaviour { private RadialBlur _component; private float _initialBlurRadius; private float _initialCenterX; private float _initialCenterY; private bool _resolved; private readonly List _activeShakes = new List(); private void Awake() { _resolved = TryResolve(); } private void OnEnable() { RadialBlurShakeEvent.Register(OnShakeEvent); } private void OnDisable() { RadialBlurShakeEvent.Unregister(OnShakeEvent); StopAll(); } private void Update() { if (!_resolved || _activeShakes.Count == 0) return; float additiveDelta = 0f; float absoluteTarget = 0f; bool hasAbsolute = false; Vector2 latestCenter = new Vector2(_initialCenterX, _initialCenterY); bool hasCenter = false; for (int i = _activeShakes.Count - 1; i >= 0; i--) { RadialBlurShakeInstance shake = _activeShakes[i]; shake.Tick(); float normalizedTime = shake.timer / shake.duration; if (shake.intensityCurve.relativeToInitial) { // 相对模式:累加曲线值 additiveDelta += shake.intensityCurve.Evaluate(normalizedTime); } else { // 绝对模式:使用曲线值作为目标值 absoluteTarget = shake.intensityCurve.Evaluate(normalizedTime); hasAbsolute = true; } if (shake.modifyCenter) { latestCenter = shake.center; hasCenter = true; } if (shake.IsFinished) { _activeShakes.RemoveAt(i); } } float finalRadius = hasAbsolute ? absoluteTarget : _initialBlurRadius + additiveDelta; _component.blurRadius.value = finalRadius; if (hasCenter) { _component.radialCenterX.value = latestCenter.x; _component.radialCenterY.value = latestCenter.y; } if (_activeShakes.Count == 0) { Restore(); } } private void OnShakeEvent( FeedbackContext feedbackContext, FloatCurveChannel intensityCurve, bool modifyCenter, Vector2 center, bool stop) { if (stop) { StopAll(); return; } if (!_resolved) _resolved = TryResolve(); if (!_resolved) return; var instance = new RadialBlurShakeInstance(feedbackContext, intensityCurve, modifyCenter, center); _activeShakes.Add(instance); } private bool TryResolve() { if (_component != null) return true; if (PostProcessingManager.Instance == null) return false; if (!PostProcessingManager.Instance.GetVolumeComponent(out _component)) return false; _initialBlurRadius = _component.blurRadius.value; _initialCenterX = _component.radialCenterX.value; _initialCenterY = _component.radialCenterY.value; return true; } private void Restore() { if (!_resolved) return; _component.blurRadius.value = _initialBlurRadius; _component.radialCenterX.value = _initialCenterX; _component.radialCenterY.value = _initialCenterY; } private void StopAll() { _activeShakes.Clear(); Restore(); } } }