Files
Cielonos/Assets/Scripts/MainGame/Effects/Feedbacks/Shakers/AnimeACESShaker.cs
SoulliesOfficial 0902ca8a9e Bezi回来了
2026-04-28 15:46:32 -04:00

274 lines
9.1 KiB
C#

using System.Collections.Generic;
using SLSUtilities.Feedback;
using SLSUtilities.Rendering.PostProcessing;
using UnityEngine;
namespace Cielonos.MainGame.Effects.Feedback
{
/// <summary>
/// Anime ACES 震动事件。
/// </summary>
public struct AnimeACESShakeEvent
{
private static event ShakeDelegate OnEvent;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void RuntimeInitialization() { OnEvent = null; }
public delegate void ShakeDelegate(
FeedbackContext feedbackContext,
bool modifyExposure,
FloatCurveChannel exposureCurve,
bool modifyContrast,
FloatCurveChannel contrastCurve,
bool modifySaturation,
FloatCurveChannel saturationCurve,
bool modifyHue,
FloatCurveChannel hueCurve,
bool modifyColorFilter,
ColorCurveChannel colorFilterCurve,
bool stop
);
public static void Register(ShakeDelegate callback) { OnEvent += callback; }
public static void Unregister(ShakeDelegate callback) { OnEvent -= callback; }
public static void Trigger(
FeedbackContext feedbackContext,
bool modifyExposure = false,
FloatCurveChannel exposureCurve = default,
bool modifyContrast = false,
FloatCurveChannel contrastCurve = default,
bool modifySaturation = false,
FloatCurveChannel saturationCurve = default,
bool modifyHue = false,
FloatCurveChannel hueCurve = default,
bool modifyColorFilter = false,
ColorCurveChannel colorFilterCurve = default,
bool stop = false)
{
OnEvent?.Invoke(
feedbackContext,
modifyExposure,
exposureCurve,
modifyContrast,
contrastCurve,
modifySaturation,
saturationCurve,
modifyHue,
hueCurve,
modifyColorFilter,
colorFilterCurve,
stop
);
}
}
/// <summary>
/// Anime ACES 震动实例。
/// </summary>
public class AnimeACESShakeInstance : ShakeInstanceBase
{
public readonly bool modifyExposure;
public readonly FloatCurveChannel exposureCurve;
public readonly bool modifyContrast;
public readonly FloatCurveChannel contrastCurve;
public readonly bool modifySaturation;
public readonly FloatCurveChannel saturationCurve;
public readonly bool modifyHue;
public readonly FloatCurveChannel hueCurve;
public readonly bool modifyColorFilter;
public readonly ColorCurveChannel colorFilterCurve;
public AnimeACESShakeInstance(
FeedbackContext feedbackContext,
bool modifyExposure,
FloatCurveChannel exposureCurve,
bool modifyContrast,
FloatCurveChannel contrastCurve,
bool modifySaturation,
FloatCurveChannel saturationCurve,
bool modifyHue,
FloatCurveChannel hueCurve,
bool modifyColorFilter,
ColorCurveChannel colorFilterCurve)
: base(feedbackContext.timeSettings, feedbackContext.player.TimeProvider, feedbackContext.duration)
{
this.modifyExposure = modifyExposure;
this.modifyContrast = modifyContrast;
this.modifySaturation = modifySaturation;
this.modifyHue = modifyHue;
this.modifyColorFilter = modifyColorFilter;
this.exposureCurve = exposureCurve;
this.contrastCurve = contrastCurve;
this.saturationCurve = saturationCurve;
this.hueCurve = hueCurve;
this.colorFilterCurve = colorFilterCurve;
}
}
/// <summary>
/// AnimeACES 的震动聚合器。
/// </summary>
[AddComponentMenu("SLS Utilities/Feedback Shakers/Anime ACES Shaker")]
public class AnimeACESShaker : MonoBehaviour
{
private AnimeACES _component;
private float _initExposure;
private float _initContrast;
private float _initSaturation;
private float _initHue;
private Color _initColorFilter;
private bool _resolved;
private readonly List<AnimeACESShakeInstance> _activeShakes = new List<AnimeACESShakeInstance>();
private void Awake()
{
_resolved = TryResolve();
}
private void OnEnable()
{
AnimeACESShakeEvent.Register(OnShakeEvent);
}
private void OnDisable()
{
AnimeACESShakeEvent.Unregister(OnShakeEvent);
StopAll();
}
private void Update()
{
if (!_resolved || _activeShakes.Count == 0) return;
float additiveExposure = 0f;
float additiveContrast = 0f;
float additiveSaturation = 0f;
float additiveHue = 0f;
Color colorFilterAccum = Color.white;
bool hasColorFilter = false;
for (int i = _activeShakes.Count - 1; i >= 0; i--)
{
AnimeACESShakeInstance shake = _activeShakes[i];
shake.Tick();
float normalizedTime = shake.timer / shake.duration;
// Exposure
if (shake.modifyExposure)
{
additiveExposure += shake.exposureCurve.Evaluate(normalizedTime);
Debug.Log($"Exposure shake: {additiveExposure}");
}
// Contrast
if (shake.modifyContrast)
{
additiveContrast += shake.contrastCurve.Evaluate(normalizedTime);
}
// Saturation
if (shake.modifySaturation)
{
additiveSaturation += shake.saturationCurve.Evaluate(normalizedTime);
}
// Hue
if (shake.modifyHue)
{
additiveHue += shake.hueCurve.Evaluate(normalizedTime);
}
// Color Filter
if (shake.modifyColorFilter)
{
colorFilterAccum = shake.colorFilterCurve.Evaluate(normalizedTime);
hasColorFilter = true;
}
if (shake.IsFinished)
{
_activeShakes.RemoveAt(i);
}
}
_component.exposure.value = _initExposure + additiveExposure;
_component.contrast.value = _initContrast + additiveContrast;
_component.saturation.value = _initSaturation + additiveSaturation;
_component.huePreservation.value = _initHue + additiveHue;
if (hasColorFilter) _component.colorFilter.value = colorFilterAccum;
if (_activeShakes.Count == 0)
{
Restore();
}
}
private void OnShakeEvent(
FeedbackContext feedbackContext,
bool modifyExposure,
FloatCurveChannel exposureCurve,
bool modifyContrast,
FloatCurveChannel contrastCurve,
bool modifySaturation,
FloatCurveChannel saturationCurve,
bool modifyHue,
FloatCurveChannel hueCurve,
bool modifyColorFilter,
ColorCurveChannel colorFilterCurve,
bool stop)
{
if (stop) { StopAll(); return; }
if (!_resolved) _resolved = TryResolve();
if (!_resolved) return;
var instance = new AnimeACESShakeInstance(
feedbackContext,
modifyExposure, exposureCurve,
modifyContrast, contrastCurve,
modifySaturation, saturationCurve,
modifyHue, hueCurve,
modifyColorFilter, colorFilterCurve);
_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;
_initExposure = _component.exposure.value;
_initContrast = _component.contrast.value;
_initSaturation = _component.saturation.value;
_initHue = _component.huePreservation.value;
_initColorFilter = _component.colorFilter.value;
return true;
}
private void Restore()
{
if (!_resolved) return;
_component.exposure.value = _initExposure;
_component.contrast.value = _initContrast;
_component.saturation.value = _initSaturation;
_component.huePreservation.value = _initHue;
_component.colorFilter.value = _initColorFilter;
}
private void StopAll()
{
_activeShakes.Clear();
Restore();
}
}
}