Files
Cielonos/Assets/Scripts/MainGame/Effects/Feedbacks/Actions/Time/TimeScaleModifierAction.cs
SoulliesOfficial 39b43680a9 爆更
2026-07-18 03:16:20 -04:00

188 lines
5.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using Sirenix.OdinInspector;
using SLSUtilities.Feedback;
using UnityEngine;
namespace Cielonos.MainGame.Effects.Feedback
{
/// <summary>
/// TimeScale 通道的工作模式。
/// </summary>
public enum TimeScaleMode
{
/// <summary>
/// 在 Clip 持续期间使用固定 TimeScale。
/// </summary>
Fixed,
/// <summary>
/// 根据 Curve 和 Remap 动态计算 TimeScale。
/// </summary>
Dynamic
}
/// <summary>
/// 单个 TimeScale 通道的配置。
/// </summary>
[Serializable]
public class TimeScaleChannel
{
/// <summary>
/// 是否启用此通道。
/// </summary>
[HorizontalGroup("Channel")]
public bool active;
/// <summary>
/// 通道的计算模式。
/// </summary>
[ShowIf("active")]
[HorizontalGroup("Channel")]
public TimeScaleMode mode = TimeScaleMode.Fixed;
/// <summary>
/// Fixed 模式下的 TimeScale 值。
/// </summary>
[ShowIf("@active && mode == TimeScaleMode.Fixed")]
[LabelText("Fixed Value")]
public float fixedValue;
/// <summary>
/// Dynamic 模式下使用的曲线。
/// </summary>
[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)
);
/// <summary>
/// Curve 值为 0 时映射到的 TimeScale。
/// </summary>
[ShowIf("@active && mode == TimeScaleMode.Dynamic")]
[LabelText("Remap Zero")]
[HorizontalGroup("Ramp")]
public float remapZero;
/// <summary>
/// Curve 值为 1 时映射到的 TimeScale。
/// </summary>
[ShowIf("@active && mode == TimeScaleMode.Dynamic")]
[LabelText("Remap One")]
[HorizontalGroup("Ramp")]
public float remapOne = 1f;
/// <summary>
/// 转换为事件传输用的只读数据。
/// </summary>
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);
}
}
/// <summary>
/// TimeScale 修改反馈。它只提交请求,实际的多实例合成由 TimeScaleShaker 统一处理。
/// </summary>
[Serializable]
[FeedbackActionColor(0.3f, 0.7f, 1.0f)]
public class TimeScaleModifierAction : FeedbackActionBase
{
public override string DisplayName => "Time Scale Modifier";
/// <summary>
/// 此 Action 自身必须使用未缩放时间推进,避免被它制造的慢动作拖慢。
/// </summary>
public override bool IgnoreTimeScale => true;
[LabelText("Priority")]
[Tooltip("同一 TimeScale 通道存在多个活跃反馈时,优先使用 Priority 更高的反馈Priority 相同则后触发者生效。")]
public int priority;
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)
{
TimeScaleShakeEvent.Trigger(
feedbackContext: context,
duration: context.duration,
priority: priority,
global: globalChannel.ToChannelData(),
player: playerChannel.ToChannelData(),
enemy: enemyChannel.ToChannelData(),
allied: alliedChannel.ToChannelData(),
nonPlayer: nonPlayerChannel.ToChannelData()
);
}
public override void OnUpdate(FeedbackContext context, float normalizedTime)
{
// TimeScaleShaker 会在收到事件后立即应用,并在之后逐帧推进。
}
public override void OnEnd(FeedbackContext context)
{
// TimeScaleShaker 根据持续时间自动结束实例。
}
public override void OnInterrupt(FeedbackContext context)
{
TimeScaleShakeEvent.Trigger(context, 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;
}
}
}