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