274 lines
9.1 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|