using System.Collections.Generic; using SLSUtilities.Feedback; using SLSUtilities.Rendering.PostProcessing; using UnityEngine; namespace Cielonos.MainGame.Effects.Feedback { /// /// AnimeBloom 震动事件。由 BloomAction 触发,AnimeBloomShaker 监听。 /// public struct AnimeBloomShakeEvent { private static event ShakeDelegate OnEvent; [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void RuntimeInitialization() { OnEvent = null; } public delegate void ShakeDelegate( FeedbackContext feedbackContext, FloatCurveChannel intensityCurve, bool modifyThreshold, FloatCurveChannel thresholdCurve, float threshold, bool modifyScatter, FloatCurveChannel scatterCurve, float scatter, bool modifyTint, ColorCurveChannel tintCurve, Color tint, 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 modifyThreshold = false, FloatCurveChannel thresholdCurve = default, float threshold = 1.1f, bool modifyScatter = false, FloatCurveChannel scatterCurve = default, float scatter = 0.7f, bool modifyTint = false, ColorCurveChannel tintCurve = default, Color tint = default, bool stop = false) { OnEvent?.Invoke(feedbackContext, intensityCurve, modifyThreshold, thresholdCurve, threshold, modifyScatter, scatterCurve, scatter, modifyTint, tintCurve, tint, stop); } } /// /// AnimeBloom 震动实例。 /// public class AnimeBloomShakeInstance : ShakeInstanceBase { public readonly FloatCurveChannel intensityCurve; public readonly bool modifyThreshold; public readonly FloatCurveChannel thresholdCurve; public readonly float threshold; public readonly bool modifyScatter; public readonly FloatCurveChannel scatterCurve; public readonly float scatter; public readonly bool modifyTint; public readonly ColorCurveChannel tintCurve; public readonly Color tint; public AnimeBloomShakeInstance( FeedbackContext feedbackContext, FloatCurveChannel intensityCurve, bool modifyThreshold, FloatCurveChannel thresholdCurve, float threshold, bool modifyScatter, FloatCurveChannel scatterCurve, float scatter, bool modifyTint, ColorCurveChannel tintCurve, Color tint) : base(feedbackContext.timeSettings, feedbackContext.player.TimeProvider, feedbackContext.duration) { this.intensityCurve = intensityCurve; this.modifyThreshold = modifyThreshold; this.thresholdCurve = thresholdCurve; this.threshold = threshold; this.modifyScatter = modifyScatter; this.scatterCurve = scatterCurve; this.scatter = scatter; this.modifyTint = modifyTint; this.tintCurve = tintCurve; this.tint = tint; } } /// /// AnimeBloom 的震动聚合器。监听 AnimeBloomShakeEvent,驱动 AnimeBloom Volume 组件的参数动画。 /// 默认驱动 intensity,可选驱动 threshold、scatter、tint。 /// [AddComponentMenu("SLS Utilities/Feedback Shakers/Anime Bloom Shaker")] public class AnimeBloomShaker : MonoBehaviour { private AnimeBloom _component; private float _initialIntensity; private float _initialThreshold; private float _initialScatter; private Color _initialTint; private bool _resolved; private readonly List _activeShakes = new List(); private void Awake() { _resolved = TryResolve(); } private void OnEnable() { AnimeBloomShakeEvent.Register(OnShakeEvent); } private void OnDisable() { AnimeBloomShakeEvent.Unregister(OnShakeEvent); StopAll(); } private void Update() { if (!_resolved || _activeShakes.Count == 0) return; float additiveIntensity = 0f; float absoluteIntensity = 0f; bool hasAbsolute = false; float latestThreshold = _initialThreshold; float latestScatter = _initialScatter; Color latestTint = _initialTint; bool hasThreshold = false; bool hasScatter = false; bool hasTint = false; for (int i = _activeShakes.Count - 1; i >= 0; i--) { AnimeBloomShakeInstance shake = _activeShakes[i]; shake.timer += shake.timeProvider.GetDeltaTime(shake.timeSettings); float normalizedTime = shake.timer / shake.duration; float curveValue = shake.intensityCurve.Evaluate(normalizedTime); if (shake.intensityCurve.relativeToInitial) additiveIntensity += curveValue; else { absoluteIntensity = curveValue; hasAbsolute = true; } if (shake.modifyThreshold) { latestThreshold = shake.thresholdCurve.Evaluate(normalizedTime); hasThreshold = true; } if (shake.modifyScatter) { latestScatter = shake.scatterCurve.Evaluate(normalizedTime); hasScatter = true; } if (shake.modifyTint) { latestTint = shake.tintCurve.Evaluate(normalizedTime); hasTint = true; } if (shake.IsFinished) { _activeShakes.RemoveAt(i); } } float finalIntensity = hasAbsolute ? absoluteIntensity : _initialIntensity + additiveIntensity; _component.intensity.value = finalIntensity; if (hasThreshold) _component.threshold.value = latestThreshold; if (hasScatter) _component.scatter.value = latestScatter; if (hasTint) _component.tint.value = latestTint; if (_activeShakes.Count == 0) { Restore(); } } private void OnShakeEvent( FeedbackContext feedbackContext, FloatCurveChannel intensityCurve, bool modifyThreshold, FloatCurveChannel thresholdCurve, float threshold, bool modifyScatter, FloatCurveChannel scatterCurve, float scatter, bool modifyTint, ColorCurveChannel tintCurve, Color tint, bool stop) { if (stop) { StopAll(); return; } if (!_resolved) _resolved = TryResolve(); if (!_resolved) return; var instance = new AnimeBloomShakeInstance( feedbackContext, intensityCurve, modifyThreshold, thresholdCurve, threshold, modifyScatter, scatterCurve, scatter, modifyTint, tintCurve, tint ); _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; _initialIntensity = _component.intensity.value; _initialThreshold = _component.threshold.value; _initialScatter = _component.scatter.value; _initialTint = _component.tint.value; return true; } private void Restore() { if (!_resolved) return; _component.intensity.value = _initialIntensity; _component.threshold.value = _initialThreshold; _component.scatter.value = _initialScatter; _component.tint.value = _initialTint; } private void StopAll() { _activeShakes.Clear(); Restore(); } } }