更新
This commit is contained in:
@@ -8,29 +8,36 @@ using UnityEngine;
|
||||
|
||||
namespace Continentis.MainGame.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// 播放角色动画的命令。
|
||||
/// 优先使用 ICharacterAnimator 接口驱动,同时保留帧精确事件轮询能力。
|
||||
/// 当角色使用 FrameAnimator(AnimatorPlus2D)时支持按帧/归一化时间触发 Action。
|
||||
/// </summary>
|
||||
public class Cmd_PlayAnimation : CommandBase
|
||||
{
|
||||
private readonly CombatCharacterViewBase characterView;
|
||||
private readonly Animator animator;
|
||||
private readonly ICharacterAnimator characterAnimator;
|
||||
private readonly bool waitForFinish;
|
||||
private readonly float overrideDuration;
|
||||
private readonly string animationName;
|
||||
private readonly int layer;
|
||||
|
||||
private AnimationClip clip;
|
||||
private float ClipScaledLength => clip.length / animator.speed;
|
||||
private float ClipLength => clip != null ? clip.length : DefaultAnimationDuration;
|
||||
private readonly Dictionary<float, Action> animationActions = new Dictionary<float, Action>();
|
||||
|
||||
private const float DefaultAnimationDuration = 0.5f;
|
||||
|
||||
public Cmd_PlayAnimation(CombatCharacterViewBase characterView, string animationName,
|
||||
bool waitForFinish = true, float overrideDuration = -1f, int layer = 0)
|
||||
{
|
||||
this.characterView = characterView;
|
||||
this.animator = characterView.animator;
|
||||
this.characterAnimator = characterView.CharacterAnimator;
|
||||
this.animationName = animationName;
|
||||
this.waitForFinish = waitForFinish;
|
||||
this.overrideDuration = overrideDuration;
|
||||
this.layer = layer;
|
||||
characterView.animations.TryGetValue(animationName, out clip);
|
||||
|
||||
// 从 CharacterData 获取 AnimationClip 引用(用于计算时长和帧事件)
|
||||
characterView.character.data.animations.TryGetValue(animationName, out clip);
|
||||
}
|
||||
|
||||
/// <summary>在动画的指定归一化时间点(0~1)执行 Action。</summary>
|
||||
@@ -66,22 +73,14 @@ namespace Continentis.MainGame.Commands
|
||||
|
||||
protected override async UniTask ExecuteAsync(CommandContext outerContext)
|
||||
{
|
||||
if (animator == null || string.IsNullOrEmpty(animationName))
|
||||
if (characterAnimator == null || string.IsNullOrEmpty(animationName))
|
||||
{
|
||||
Debug.LogWarning("[Cmd_PlayAnimation] Animator 或动画名称为空。");
|
||||
Debug.LogWarning("[Cmd_PlayAnimation] CharacterAnimator 或动画名称为空。");
|
||||
return;
|
||||
}
|
||||
|
||||
// 确认播放目标动画,回退到 "Action"
|
||||
string finalName = characterView.animations.ContainsKey(animationName) ? animationName : "Action";
|
||||
|
||||
if (!characterView.animations.TryGetValue(finalName, out clip))
|
||||
{
|
||||
Debug.LogWarning($"[Cmd_PlayAnimation] 找不到动画片段:{finalName}");
|
||||
return;
|
||||
}
|
||||
|
||||
characterView.animatorPlus2D.Play(clip);
|
||||
// 通过 ICharacterAnimator 播放
|
||||
characterAnimator.PlayAction(animationName);
|
||||
|
||||
// 帧轮询动画事件(fire-and-forget,不阻塞命令流)
|
||||
if (animationActions.Count > 0)
|
||||
@@ -89,22 +88,25 @@ namespace Continentis.MainGame.Commands
|
||||
|
||||
if (waitForFinish)
|
||||
{
|
||||
float duration = overrideDuration >= 0f ? overrideDuration / animator.speed : ClipScaledLength;
|
||||
float duration = overrideDuration >= 0f ? overrideDuration : ClipLength;
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(duration));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 基于经过时间轮询帧事件,兼容所有动画驱动器。
|
||||
/// </summary>
|
||||
private async UniTaskVoid PollAnimationActionsAsync()
|
||||
{
|
||||
float elapsed = 0f;
|
||||
float totalDuration = ClipScaledLength;
|
||||
float totalDuration = ClipLength;
|
||||
var pending = new Dictionary<float, Action>(animationActions);
|
||||
|
||||
while (elapsed < totalDuration && pending.Count > 0)
|
||||
{
|
||||
await UniTask.Yield(PlayerLoopTiming.Update);
|
||||
elapsed += Time.deltaTime;
|
||||
float normalizedTime = animator.GetCurrentAnimatorStateInfo(layer).normalizedTime % 1f;
|
||||
float normalizedTime = Mathf.Clamp01(elapsed / totalDuration);
|
||||
|
||||
foreach (float key in pending.Keys.ToList())
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user