using System.Collections.Generic;
using SLSUtilities.Feedback;
using SLSUtilities.Rendering.PostProcessing;
using UnityEngine;
namespace Cielonos.MainGame.Effects.Feedback
{
///
/// 频率控制模式。
///
public enum StrobeFrequencyMode
{
/// 使用固定频率数值。
Constant,
/// 使用 FloatCurveChannel 以动画曲线控制频率。
Curve,
/// 使用 AnimationCurve 手动控制 ManualInvert(Y >= 1 时翻转)。
Manual
}
///
/// 黑白闪震动事件。
///
public struct StrobeFlashShakeEvent
{
private static event ShakeDelegate OnEvent;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void RuntimeInitialization() { OnEvent = null; }
public delegate void ShakeDelegate(
FeedbackContext feedbackContext,
StrobeFrequencyMode frequencyMode,
float constantFrequency,
FloatCurveChannel frequencyCurve,
AnimationCurve manualCurve,
bool modifyColorHigh,
bool useColorHighGradient,
Color colorHighConstant,
ColorCurveChannel colorHighCurve,
bool modifyColorLow,
bool useColorLowGradient,
Color colorLowConstant,
ColorCurveChannel colorLowCurve,
bool stop
);
public static void Register(ShakeDelegate callback) { OnEvent += callback; }
public static void Unregister(ShakeDelegate callback) { OnEvent -= callback; }
public static void Trigger(
FeedbackContext feedbackContext,
StrobeFrequencyMode frequencyMode = StrobeFrequencyMode.Constant,
float constantFrequency = 15f,
FloatCurveChannel frequencyCurve = default,
AnimationCurve manualCurve = null,
bool modifyColorHigh = false,
bool useColorHighGradient = false,
Color colorHighConstant = default,
ColorCurveChannel colorHighCurve = default,
bool modifyColorLow = false,
bool useColorLowGradient = false,
Color colorLowConstant = default,
ColorCurveChannel colorLowCurve = default,
bool stop = false)
{
OnEvent?.Invoke(feedbackContext,
frequencyMode, constantFrequency, frequencyCurve, manualCurve,
modifyColorHigh, useColorHighGradient, colorHighConstant, colorHighCurve,
modifyColorLow, useColorLowGradient, colorLowConstant, colorLowCurve,
stop);
}
}
///
/// 黑白闪震动实例。
///
public class StrobeFlashShakeInstance : ShakeInstanceBase
{
public readonly StrobeFrequencyMode frequencyMode;
public readonly float constantFrequency;
public readonly FloatCurveChannel frequencyCurve;
public readonly AnimationCurve manualCurve;
public readonly bool modifyColorHigh;
public readonly bool useColorHighGradient;
public readonly Color colorHighConstant;
public readonly ColorCurveChannel colorHighCurve;
public readonly bool modifyColorLow;
public readonly bool useColorLowGradient;
public readonly Color colorLowConstant;
public readonly ColorCurveChannel colorLowCurve;
public StrobeFlashShakeInstance(
FeedbackContext feedbackContext,
StrobeFrequencyMode frequencyMode,
float constantFrequency,
FloatCurveChannel frequencyCurve,
AnimationCurve manualCurve,
bool modifyColorHigh,
bool useColorHighGradient,
Color colorHighConstant,
ColorCurveChannel colorHighCurve,
bool modifyColorLow,
bool useColorLowGradient,
Color colorLowConstant,
ColorCurveChannel colorLowCurve)
: base(feedbackContext.timeSettings, feedbackContext.player.TimeProvider, feedbackContext.duration)
{
this.frequencyMode = frequencyMode;
this.constantFrequency = constantFrequency;
this.frequencyCurve = frequencyCurve;
this.manualCurve = manualCurve;
this.modifyColorHigh = modifyColorHigh;
this.useColorHighGradient = useColorHighGradient;
this.colorHighConstant = colorHighConstant;
this.colorHighCurve = colorHighCurve;
this.modifyColorLow = modifyColorLow;
this.useColorLowGradient = useColorLowGradient;
this.colorLowConstant = colorLowConstant;
this.colorLowCurve = colorLowCurve;
}
}
///
/// StrobeFlash 的震动聚合器。最新添加的 Shake 具有最高优先级。
///
[AddComponentMenu("SLS Utilities/Feedback Shakers/Strobe Flash Shaker")]
public class StrobeFlashShaker : MonoBehaviour
{
private StrobeFlash _component;
private float _initialFrequency;
private Color _initialColorHigh;
private Color _initialColorLow;
private bool _resolved;
private readonly List _activeShakes = new List();
private void Awake()
{
_resolved = TryResolve();
}
private void OnEnable()
{
StrobeFlashShakeEvent.Register(OnShakeEvent);
}
private void OnDisable()
{
StrobeFlashShakeEvent.Unregister(OnShakeEvent);
StopAll();
}
private void Update()
{
if (!_resolved || _activeShakes.Count == 0) return;
for (int i = _activeShakes.Count - 1; i >= 0; i--)
{
_activeShakes[i].timer += _activeShakes[i].timeProvider.GetDeltaTime(_activeShakes[i].timeSettings);
if (_activeShakes[i].IsFinished)
_activeShakes.RemoveAt(i);
}
if (_activeShakes.Count == 0)
{
Restore();
return;
}
// 最新添加(最高索引)的 Shake 具有最高优先级。
StrobeFlashShakeInstance dominant = _activeShakes[_activeShakes.Count - 1];
float normalizedTime = dominant.timer / dominant.duration;
_component.enableEffect.value = true;
switch (dominant.frequencyMode)
{
case StrobeFrequencyMode.Constant:
_component.autoFlash.value = true;
_component.manualInvert.value = false;
_component.frequency.value = dominant.constantFrequency;
break;
case StrobeFrequencyMode.Curve:
_component.autoFlash.value = true;
_component.manualInvert.value = false;
_component.frequency.value = dominant.frequencyCurve.Evaluate(normalizedTime);
break;
case StrobeFrequencyMode.Manual:
_component.autoFlash.value = false;
_component.manualInvert.value = dominant.manualCurve.Evaluate(normalizedTime) >= 1f;
break;
}
if (dominant.modifyColorHigh)
{
_component.colorHigh.value = dominant.useColorHighGradient
? dominant.colorHighCurve.Evaluate(normalizedTime)
: dominant.colorHighConstant;
}
if (dominant.modifyColorLow)
{
_component.colorLow.value = dominant.useColorLowGradient
? dominant.colorLowCurve.Evaluate(normalizedTime)
: dominant.colorLowConstant;
}
}
private void OnShakeEvent(
FeedbackContext feedbackContext,
StrobeFrequencyMode frequencyMode,
float constantFrequency,
FloatCurveChannel frequencyCurve,
AnimationCurve manualCurve,
bool modifyColorHigh,
bool useColorHighGradient,
Color colorHighConstant,
ColorCurveChannel colorHighCurve,
bool modifyColorLow,
bool useColorLowGradient,
Color colorLowConstant,
ColorCurveChannel colorLowCurve,
bool stop)
{
if (stop) { StopAll(); return; }
if (!_resolved) _resolved = TryResolve();
if (!_resolved) return;
var instance = new StrobeFlashShakeInstance(
feedbackContext,
frequencyMode, constantFrequency, frequencyCurve, manualCurve,
modifyColorHigh, useColorHighGradient, colorHighConstant, colorHighCurve,
modifyColorLow, useColorLowGradient, colorLowConstant, colorLowCurve
);
_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;
_initialFrequency = _component.frequency.value;
_initialColorHigh = _component.colorHigh.value;
_initialColorLow = _component.colorLow.value;
return true;
}
private void Restore()
{
if (!_resolved) return;
_component.enableEffect.value = false;
_component.autoFlash.value = false;
_component.manualInvert.value = false;
_component.frequency.value = _initialFrequency;
_component.colorHigh.value = _initialColorHigh;
_component.colorLow.value = _initialColorLow;
}
private void StopAll()
{
_activeShakes.Clear();
Restore();
}
}
}