209 lines
7.0 KiB
C#
209 lines
7.0 KiB
C#
using System;
|
||
using Cielonos.MainGame;
|
||
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>
|
||
public bool active;
|
||
|
||
/// <summary>
|
||
/// 通道工作模式。
|
||
/// </summary>
|
||
[ShowIf("active")]
|
||
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")]
|
||
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")]
|
||
public float remapZero;
|
||
|
||
/// <summary>
|
||
/// 曲线值 1 映射到的实际值。
|
||
/// </summary>
|
||
[ShowIf("@active && mode == TimeScaleMode.Dynamic")]
|
||
[LabelText("Remap One")]
|
||
public float remapOne = 1f;
|
||
|
||
/// <summary>
|
||
/// 根据归一化进度计算当前通道的时间缩放值。
|
||
/// </summary>
|
||
public float Evaluate(float normalizedTime)
|
||
{
|
||
if (!active) return 1f;
|
||
|
||
if (mode == TimeScaleMode.Fixed)
|
||
{
|
||
return fixedValue;
|
||
}
|
||
|
||
float curveValue = curve.Evaluate(normalizedTime);
|
||
return Mathf.LerpUnclamped(remapZero, remapOne, curveValue);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 时间缩放修改器反馈,直接驱动 TimeManager 的各个通道。
|
||
///
|
||
/// 重要:此 Action 只应使用游戏的 unscaledDeltaTime 驱动。
|
||
/// 不要在包含此 Action 的 Clip 上启用自定义 overrideTimeSettings,
|
||
/// FeedbackData 的 defaultTimeSettings.useTimeScale 也应保持为 false。
|
||
/// 我们的自定义时间参数绝不能影响时间缩放修改器本身。
|
||
/// </summary>
|
||
[Serializable]
|
||
public class TimeScaleModifierAction : FeedbackActionBase
|
||
{
|
||
public override string DisplayName => "Time Scale Modifier";
|
||
|
||
[Title("Global Time Scale")]
|
||
public TimeScaleChannel globalChannel = new TimeScaleChannel { active = true, fixedValue = 0f };
|
||
|
||
[Title("Player Time Scale")]
|
||
public TimeScaleChannel playerChannel = new TimeScaleChannel();
|
||
|
||
[Title("Enemy Time Scale")]
|
||
public TimeScaleChannel enemyChannel = new TimeScaleChannel();
|
||
|
||
[Title("Allied Time Scale")]
|
||
public TimeScaleChannel alliedChannel = new TimeScaleChannel();
|
||
|
||
[Title("Non-Player Time Scale")]
|
||
public TimeScaleChannel nonPlayerChannel = new TimeScaleChannel();
|
||
|
||
[NonSerialized] private float _initialGlobal;
|
||
[NonSerialized] private float _initialPlayer;
|
||
[NonSerialized] private float _initialEnemy;
|
||
[NonSerialized] private float _initialAllied;
|
||
[NonSerialized] private float _initialNonPlayer;
|
||
|
||
public override void OnStart(FeedbackContext context)
|
||
{
|
||
if (TimeManager.Instance == null)
|
||
{
|
||
Debug.LogWarning("[TimeScaleModifierAction] TimeManager instance not found.");
|
||
return;
|
||
}
|
||
|
||
_initialGlobal = TimeManager.Instance.globalTimeScale.Value;
|
||
_initialPlayer = TimeManager.Instance.playerTimeScale.Value;
|
||
_initialEnemy = TimeManager.Instance.enemyTimeScale.Value;
|
||
_initialAllied = TimeManager.Instance.alliedMinionTimeScale.Value;
|
||
_initialNonPlayer = TimeManager.Instance.nonPlayerTimeScale.Value;
|
||
}
|
||
|
||
public override void OnUpdate(FeedbackContext context, float normalizedTime)
|
||
{
|
||
if (TimeManager.Instance == null) return;
|
||
|
||
if (globalChannel.active)
|
||
TimeManager.Instance.globalTimeScale.Value = globalChannel.Evaluate(normalizedTime);
|
||
|
||
if (playerChannel.active)
|
||
TimeManager.Instance.playerTimeScale.Value = playerChannel.Evaluate(normalizedTime);
|
||
|
||
if (enemyChannel.active)
|
||
TimeManager.Instance.enemyTimeScale.Value = enemyChannel.Evaluate(normalizedTime);
|
||
|
||
if (alliedChannel.active)
|
||
TimeManager.Instance.alliedMinionTimeScale.Value = alliedChannel.Evaluate(normalizedTime);
|
||
|
||
if (nonPlayerChannel.active)
|
||
TimeManager.Instance.nonPlayerTimeScale.Value = nonPlayerChannel.Evaluate(normalizedTime);
|
||
}
|
||
|
||
public override void OnEnd(FeedbackContext context)
|
||
{
|
||
RestoreValues();
|
||
}
|
||
|
||
public override void OnInterrupt(FeedbackContext context)
|
||
{
|
||
RestoreValues();
|
||
}
|
||
|
||
public override bool Validate(out string error)
|
||
{
|
||
// 防呆检查:时间缩放修改器不应受自定义时间缩放影响
|
||
// 此检查在 Editor 中调用,完整的 Inspector 防呆将在后续版本中添加
|
||
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;
|
||
}
|
||
|
||
private void RestoreValues()
|
||
{
|
||
if (TimeManager.Instance == null) return;
|
||
|
||
if (globalChannel.active)
|
||
TimeManager.Instance.globalTimeScale.Value = _initialGlobal;
|
||
|
||
if (playerChannel.active)
|
||
TimeManager.Instance.playerTimeScale.Value = _initialPlayer;
|
||
|
||
if (enemyChannel.active)
|
||
TimeManager.Instance.enemyTimeScale.Value = _initialEnemy;
|
||
|
||
if (alliedChannel.active)
|
||
TimeManager.Instance.alliedMinionTimeScale.Value = _initialAllied;
|
||
|
||
if (nonPlayerChannel.active)
|
||
TimeManager.Instance.nonPlayerTimeScale.Value = _initialNonPlayer;
|
||
}
|
||
}
|
||
}
|