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();
}
}
}