using System;
using System.Collections.Generic;
using Cielonos.MainGame;
using UnityEngine;
namespace Cielonos.MainGame.Effects.Feedback
{
///
/// 时间缩放通道数据,用于事件传输。
///
[Serializable]
public struct TimeScaleChannelData
{
public bool active;
public TimeScaleMode mode;
public float fixedValue;
public AnimationCurve curve;
public float remapZero;
public float remapOne;
///
/// 根据归一化进度计算当前通道的时间缩放值。
///
public float Evaluate(float normalizedTime)
{
if (!active) return 1f;
if (mode == TimeScaleMode.Fixed)
{
return fixedValue;
}
float curveValue = curve != null ? curve.Evaluate(normalizedTime) : 0f;
return Mathf.LerpUnclamped(remapZero, remapOne, curveValue);
}
}
///
/// 时间缩放震动事件。
///
public struct TimeScaleShakeEvent
{
private static event ShakeDelegate OnEvent;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void RuntimeInitialization() { OnEvent = null; }
public delegate void ShakeDelegate(
float duration,
TimeScaleChannelData global,
TimeScaleChannelData player,
TimeScaleChannelData enemy,
TimeScaleChannelData allied,
TimeScaleChannelData nonPlayer,
bool stop
);
///
/// 注册震动监听。
///
public static void Register(ShakeDelegate callback) { OnEvent += callback; }
///
/// 取消震动监听。
///
public static void Unregister(ShakeDelegate callback) { OnEvent -= callback; }
///
/// 触发时间缩放震动事件。
///
public static void Trigger(
float duration,
TimeScaleChannelData global = default,
TimeScaleChannelData player = default,
TimeScaleChannelData enemy = default,
TimeScaleChannelData allied = default,
TimeScaleChannelData nonPlayer = default,
bool stop = false)
{
OnEvent?.Invoke(duration, global, player, enemy, allied, nonPlayer, stop);
}
}
///
/// 时间缩放震动实例。
///
public class TimeScaleShakeInstance
{
public readonly float Duration;
public readonly TimeScaleChannelData Global;
public readonly TimeScaleChannelData Player;
public readonly TimeScaleChannelData Enemy;
public readonly TimeScaleChannelData Allied;
public readonly TimeScaleChannelData NonPlayer;
public float Timer;
public TimeScaleShakeInstance(
float duration,
TimeScaleChannelData global,
TimeScaleChannelData player,
TimeScaleChannelData enemy,
TimeScaleChannelData allied,
TimeScaleChannelData nonPlayer)
{
Duration = duration;
Global = global;
Player = player;
Enemy = enemy;
Allied = allied;
NonPlayer = nonPlayer;
Timer = 0f;
}
public bool IsFinished => Timer >= Duration;
}
///
/// TimeManager 的时间缩放震动聚合器。
/// 管理多个并发时间缩放实例。
/// 当有活跃实例时,各通道取"最后激活"实例的值(last wins)。
/// 全部结束后恢复初始值。
///
[AddComponentMenu("SLS Utilities/Feedback Shakers/Time Scale Shaker")]
public class TimeScaleShaker : MonoBehaviour
{
private float _initGlobal;
private float _initPlayer;
private float _initEnemy;
private float _initAllied;
private float _initNonPlayer;
private bool _resolved;
private readonly List _activeShakes =
new List();
private void Awake()
{
_resolved = TryResolve();
}
private void OnEnable()
{
TimeScaleShakeEvent.Register(OnShakeEvent);
}
private void OnDisable()
{
TimeScaleShakeEvent.Unregister(OnShakeEvent);
StopAll();
}
private void Update()
{
if (!_resolved || _activeShakes.Count == 0) return;
if (TimeManager.Instance == null) return;
float dt = Time.deltaTime;
// 各通道取最后激活实例的值
float globalVal = _initGlobal;
float playerVal = _initPlayer;
float enemyVal = _initEnemy;
float alliedVal = _initAllied;
float nonPlayerVal = _initNonPlayer;
bool hasGlobal = false, hasPlayer = false, hasEnemy = false;
bool hasAllied = false, hasNonPlayer = false;
for (int i = _activeShakes.Count - 1; i >= 0; i--)
{
TimeScaleShakeInstance shake = _activeShakes[i];
shake.Timer += dt;
float t = Mathf.Clamp01(shake.Timer / shake.Duration);
if (shake.Global.active) { globalVal = shake.Global.Evaluate(t); hasGlobal = true; }
if (shake.Player.active) { playerVal = shake.Player.Evaluate(t); hasPlayer = true; }
if (shake.Enemy.active) { enemyVal = shake.Enemy.Evaluate(t); hasEnemy = true; }
if (shake.Allied.active) { alliedVal = shake.Allied.Evaluate(t); hasAllied = true; }
if (shake.NonPlayer.active) { nonPlayerVal = shake.NonPlayer.Evaluate(t); hasNonPlayer = true; }
if (shake.IsFinished)
{
_activeShakes.RemoveAt(i);
}
}
if (hasGlobal) TimeManager.Instance.globalTimeScale.Value = globalVal;
if (hasPlayer) TimeManager.Instance.playerTimeScale.Value = playerVal;
if (hasEnemy) TimeManager.Instance.enemyTimeScale.Value = enemyVal;
if (hasAllied) TimeManager.Instance.alliedMinionTimeScale.Value = alliedVal;
if (hasNonPlayer) TimeManager.Instance.nonPlayerTimeScale.Value = nonPlayerVal;
if (_activeShakes.Count == 0)
{
Restore();
}
}
private void OnShakeEvent(
float duration,
TimeScaleChannelData global,
TimeScaleChannelData player,
TimeScaleChannelData enemy,
TimeScaleChannelData allied,
TimeScaleChannelData nonPlayer,
bool stop)
{
if (stop)
{
StopAll();
return;
}
if (!_resolved) _resolved = TryResolve();
if (!_resolved) return;
_activeShakes.Add(new TimeScaleShakeInstance(
duration, global, player, enemy, allied, nonPlayer
));
}
private bool TryResolve()
{
if (TimeManager.Instance == null) return false;
_initGlobal = TimeManager.Instance.globalTimeScale.Value;
_initPlayer = TimeManager.Instance.playerTimeScale.Value;
_initEnemy = TimeManager.Instance.enemyTimeScale.Value;
_initAllied = TimeManager.Instance.alliedMinionTimeScale.Value;
_initNonPlayer = TimeManager.Instance.nonPlayerTimeScale.Value;
return true;
}
private void Restore()
{
if (TimeManager.Instance == null) return;
TimeManager.Instance.globalTimeScale.Value = _initGlobal;
TimeManager.Instance.playerTimeScale.Value = _initPlayer;
TimeManager.Instance.enemyTimeScale.Value = _initEnemy;
TimeManager.Instance.alliedMinionTimeScale.Value = _initAllied;
TimeManager.Instance.nonPlayerTimeScale.Value = _initNonPlayer;
}
private void StopAll()
{
_activeShakes.Clear();
Restore();
}
}
}