Files
Cielonos/Assets/Scripts/MainGame/Effects/Feedbacks/Actions/TimeScaleModifierAction.cs
SoulliesOfficial 41140a2017 新Feedback系统
2026-04-12 02:11:15 -04:00

209 lines
7.0 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 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;
}
}
}