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