2026-04-12 02:11:15 -04:00
|
|
|
|
using System;
|
|
|
|
|
|
using Sirenix.OdinInspector;
|
|
|
|
|
|
using SLSUtilities.Feedback;
|
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Cielonos.MainGame.Effects.Feedback
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 时间缩放通道的工作模式。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public enum TimeScaleMode
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 固定值模式:在 Clip 期间将时间缩放设为固定值。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
Fixed,
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 动态曲线模式:根据曲线和 Remap 驱动时间缩放。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
Dynamic
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 单个时间缩放通道的配置。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[Serializable]
|
|
|
|
|
|
public class TimeScaleChannel
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 是否激活此通道。
|
|
|
|
|
|
/// </summary>
|
2026-04-18 13:57:19 -04:00
|
|
|
|
[HorizontalGroup("Channel")]
|
2026-04-12 02:11:15 -04:00
|
|
|
|
public bool active;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 通道工作模式。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[ShowIf("active")]
|
2026-04-18 13:57:19 -04:00
|
|
|
|
[HorizontalGroup("Channel")]
|
2026-04-12 02:11:15 -04:00
|
|
|
|
public TimeScaleMode mode = TimeScaleMode.Fixed;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Fixed 模式下的目标值。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[ShowIf("@active && mode == TimeScaleMode.Fixed")]
|
|
|
|
|
|
[LabelText("Fixed Value")]
|
|
|
|
|
|
public float fixedValue;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Dynamic 模式下的变化曲线。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[ShowIf("@active && mode == TimeScaleMode.Dynamic")]
|
|
|
|
|
|
[LabelText("Curve")]
|
2026-04-18 13:57:19 -04:00
|
|
|
|
[ShakeCurvePreset]
|
2026-04-12 02:11:15 -04:00
|
|
|
|
public AnimationCurve curve = new AnimationCurve(
|
|
|
|
|
|
new Keyframe(0f, 0f),
|
|
|
|
|
|
new Keyframe(0.5f, 1f),
|
|
|
|
|
|
new Keyframe(1f, 0f)
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 曲线值 0 映射到的实际值。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[ShowIf("@active && mode == TimeScaleMode.Dynamic")]
|
|
|
|
|
|
[LabelText("Remap Zero")]
|
2026-04-18 13:57:19 -04:00
|
|
|
|
[HorizontalGroup("Ramp")]
|
2026-04-12 02:11:15 -04:00
|
|
|
|
public float remapZero;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 曲线值 1 映射到的实际值。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[ShowIf("@active && mode == TimeScaleMode.Dynamic")]
|
|
|
|
|
|
[LabelText("Remap One")]
|
2026-04-18 13:57:19 -04:00
|
|
|
|
[HorizontalGroup("Ramp")]
|
2026-04-12 02:11:15 -04:00
|
|
|
|
public float remapOne = 1f;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-04-18 13:57:19 -04:00
|
|
|
|
/// 将此通道的配置转换为事件传输用的 TimeScaleChannelData。
|
2026-04-12 02:11:15 -04:00
|
|
|
|
/// </summary>
|
2026-04-18 13:57:19 -04:00
|
|
|
|
public TimeScaleChannelData ToChannelData()
|
|
|
|
|
|
{
|
|
|
|
|
|
return new TimeScaleChannelData
|
|
|
|
|
|
{
|
|
|
|
|
|
active = active,
|
|
|
|
|
|
mode = mode,
|
|
|
|
|
|
fixedValue = fixedValue,
|
|
|
|
|
|
curve = curve,
|
|
|
|
|
|
remapZero = remapZero,
|
|
|
|
|
|
remapOne = remapOne
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-12 02:11:15 -04:00
|
|
|
|
public float Evaluate(float normalizedTime)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!active) return 1f;
|
|
|
|
|
|
|
|
|
|
|
|
if (mode == TimeScaleMode.Fixed)
|
|
|
|
|
|
{
|
|
|
|
|
|
return fixedValue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-18 13:57:19 -04:00
|
|
|
|
float curveValue = curve?.Evaluate(normalizedTime) ?? 0f;
|
2026-04-12 02:11:15 -04:00
|
|
|
|
return Mathf.LerpUnclamped(remapZero, remapOne, curveValue);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-04-18 13:57:19 -04:00
|
|
|
|
/// 时间缩放修改器反馈,通过 TimeScaleShakeEvent 触发 TimeScaleShaker。
|
|
|
|
|
|
/// Shaker 负责管理多个并发时间缩放实例的叠加混合和初始值恢复。
|
2026-04-12 02:11:15 -04:00
|
|
|
|
///
|
2026-04-18 13:57:19 -04:00
|
|
|
|
/// 重要:此 Action 会忽略时间缩放,使用未缩放的 deltaTime 驱动。
|
|
|
|
|
|
/// 当 Time.timeScale == 0 时,此 Action 也会暂停。
|
2026-04-12 02:11:15 -04:00
|
|
|
|
/// 不要在包含此 Action 的 Clip 上启用自定义 overrideTimeSettings,
|
|
|
|
|
|
/// FeedbackData 的 defaultTimeSettings.useTimeScale 也应保持为 false。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[Serializable]
|
2026-04-18 13:57:19 -04:00
|
|
|
|
[FeedbackActionColor(0.3f, 0.7f, 1.0f)]
|
2026-04-12 02:11:15 -04:00
|
|
|
|
public class TimeScaleModifierAction : FeedbackActionBase
|
|
|
|
|
|
{
|
|
|
|
|
|
public override string DisplayName => "Time Scale Modifier";
|
2026-04-18 13:57:19 -04:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 忽略时间缩放,使用未缩放的 deltaTime。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public override bool IgnoreTimeScale => true;
|
|
|
|
|
|
|
|
|
|
|
|
public TimeScaleChannel globalChannel = new TimeScaleChannel { active = true, fixedValue = 0.1f };
|
2026-04-12 02:11:15 -04:00
|
|
|
|
|
2026-04-18 13:57:19 -04:00
|
|
|
|
public bool advancedSettings = false;
|
|
|
|
|
|
|
|
|
|
|
|
[ShowIf("advancedSettings")]
|
2026-04-12 02:11:15 -04:00
|
|
|
|
public TimeScaleChannel playerChannel = new TimeScaleChannel();
|
2026-04-18 13:57:19 -04:00
|
|
|
|
|
|
|
|
|
|
[ShowIf("advancedSettings")]
|
2026-04-12 02:11:15 -04:00
|
|
|
|
public TimeScaleChannel enemyChannel = new TimeScaleChannel();
|
2026-04-18 13:57:19 -04:00
|
|
|
|
|
|
|
|
|
|
[ShowIf("advancedSettings")]
|
2026-04-12 02:11:15 -04:00
|
|
|
|
public TimeScaleChannel alliedChannel = new TimeScaleChannel();
|
2026-04-18 13:57:19 -04:00
|
|
|
|
|
|
|
|
|
|
[ShowIf("advancedSettings")]
|
2026-04-12 02:11:15 -04:00
|
|
|
|
public TimeScaleChannel nonPlayerChannel = new TimeScaleChannel();
|
|
|
|
|
|
|
|
|
|
|
|
public override void OnStart(FeedbackContext context)
|
|
|
|
|
|
{
|
2026-04-18 13:57:19 -04:00
|
|
|
|
// 通过事件触发,让TimeScaleShaker注册这个实例
|
|
|
|
|
|
TimeScaleShakeEvent.Trigger(
|
|
|
|
|
|
duration: context.duration,
|
|
|
|
|
|
global: globalChannel.ToChannelData(),
|
|
|
|
|
|
player: playerChannel.ToChannelData(),
|
|
|
|
|
|
enemy: enemyChannel.ToChannelData(),
|
|
|
|
|
|
allied: alliedChannel.ToChannelData(),
|
|
|
|
|
|
nonPlayer: nonPlayerChannel.ToChannelData()
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// 立即执行一次TimeScaleShaker的更新
|
|
|
|
|
|
// 这样在同一帧内,TimeScaleModifierAction修改的globalTimeScale就能立即生效
|
|
|
|
|
|
ImmediateApplyTimeScale();
|
2026-04-12 02:11:15 -04:00
|
|
|
|
}
|
2026-04-18 13:57:19 -04:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 立即应用时间缩放,确保在同一帧内立即生效
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void ImmediateApplyTimeScale()
|
2026-04-12 02:11:15 -04:00
|
|
|
|
{
|
|
|
|
|
|
if (TimeManager.Instance == null) return;
|
2026-04-18 13:57:19 -04:00
|
|
|
|
|
2026-04-12 02:11:15 -04:00
|
|
|
|
if (globalChannel.active)
|
2026-04-18 13:57:19 -04:00
|
|
|
|
{
|
|
|
|
|
|
TimeManager.Instance.globalTimeScale.Value = globalChannel.Evaluate(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-12 02:11:15 -04:00
|
|
|
|
if (playerChannel.active)
|
2026-04-18 13:57:19 -04:00
|
|
|
|
{
|
|
|
|
|
|
TimeManager.Instance.playerTimeScale.Value = playerChannel.Evaluate(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-12 02:11:15 -04:00
|
|
|
|
if (enemyChannel.active)
|
2026-04-18 13:57:19 -04:00
|
|
|
|
{
|
|
|
|
|
|
TimeManager.Instance.enemyTimeScale.Value = enemyChannel.Evaluate(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-12 02:11:15 -04:00
|
|
|
|
if (alliedChannel.active)
|
2026-04-18 13:57:19 -04:00
|
|
|
|
{
|
|
|
|
|
|
TimeManager.Instance.alliedMinionTimeScale.Value = alliedChannel.Evaluate(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-12 02:11:15 -04:00
|
|
|
|
if (nonPlayerChannel.active)
|
2026-04-18 13:57:19 -04:00
|
|
|
|
{
|
|
|
|
|
|
TimeManager.Instance.nonPlayerTimeScale.Value = nonPlayerChannel.Evaluate(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override void OnUpdate(FeedbackContext context, float normalizedTime)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Shaker 自行每帧驱动所有活跃实例。
|
2026-04-12 02:11:15 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override void OnEnd(FeedbackContext context)
|
|
|
|
|
|
{
|
2026-04-18 13:57:19 -04:00
|
|
|
|
// Shaker 自动管理实例生命周期和初始值恢复。
|
2026-04-12 02:11:15 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override void OnInterrupt(FeedbackContext context)
|
|
|
|
|
|
{
|
2026-04-18 13:57:19 -04:00
|
|
|
|
TimeScaleShakeEvent.Trigger(0f, stop: true);
|
2026-04-12 02:11:15 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override bool Validate(out string error)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool anyActive = globalChannel.active || playerChannel.active ||
|
|
|
|
|
|
enemyChannel.active || alliedChannel.active ||
|
|
|
|
|
|
nonPlayerChannel.active;
|
|
|
|
|
|
|
|
|
|
|
|
if (!anyActive)
|
|
|
|
|
|
{
|
|
|
|
|
|
error = "No time scale channel is active. Enable at least one channel.";
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
error = null;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|