Files
Continentis/Assets/Scripts/MainGame/Commands/Cmd_PlayAnimation.cs
SoulliesOfficial d09b58fd80 架构大更
2026-03-20 11:56:50 -04:00

120 lines
4.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 System.Collections.Generic;
using System.Linq;
using Continentis.MainGame.Character;
using Cysharp.Threading.Tasks;
using SLSFramework.General;
using UnityEngine;
namespace Continentis.MainGame.Commands
{
public class Cmd_PlayAnimation : CommandBase
{
private readonly CombatCharacterViewBase characterView;
private readonly Animator animator;
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 readonly Dictionary<float, Action> animationActions = new Dictionary<float, Action>();
public Cmd_PlayAnimation(CombatCharacterViewBase characterView, string animationName,
bool waitForFinish = true, float overrideDuration = -1f, int layer = 0)
{
this.characterView = characterView;
this.animator = characterView.animator;
this.animationName = animationName;
this.waitForFinish = waitForFinish;
this.overrideDuration = overrideDuration;
this.layer = layer;
characterView.animations.TryGetValue(animationName, out clip);
}
/// <summary>在动画的指定归一化时间点0~1执行 Action。</summary>
public Cmd_PlayAnimation AddAction(float normalizedTime, Action action)
{
animationActions[normalizedTime] = action;
return this;
}
/// <summary>在动画的指定帧执行 Action。</summary>
public Cmd_PlayAnimation AddAction(int frame, Action action)
{
if (clip == null) return this;
float normalizedTime = frame / (clip.frameRate * clip.length);
return AddAction(normalizedTime, action);
}
/// <summary>在动画的指定归一化时间点执行带强类型参数的 Action参数从 selfContext 读取。</summary>
public Cmd_PlayAnimation AddAction<T>(float normalizedTime, string selfContextKey, Action<T> action)
{
T param = selfContext.Get<T>(selfContextKey);
animationActions[normalizedTime] = () => action(param);
return this;
}
/// <summary>在动画的指定帧执行带强类型参数的 Action参数从 selfContext 读取。</summary>
public Cmd_PlayAnimation AddAction<T>(int frame, string selfContextKey, Action<T> action)
{
if (clip == null) return this;
float normalizedTime = frame / (clip.frameRate * clip.length);
return AddAction(normalizedTime, selfContextKey, action);
}
protected override async UniTask ExecuteAsync(CommandContext outerContext)
{
if (animator == null || string.IsNullOrEmpty(animationName))
{
Debug.LogWarning("[Cmd_PlayAnimation] Animator 或动画名称为空。");
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);
// 帧轮询动画事件fire-and-forget不阻塞命令流
if (animationActions.Count > 0)
PollAnimationActionsAsync().Forget();
if (waitForFinish)
{
float duration = overrideDuration >= 0f ? overrideDuration / animator.speed : ClipScaledLength;
await UniTask.Delay(TimeSpan.FromSeconds(duration));
}
}
private async UniTaskVoid PollAnimationActionsAsync()
{
float elapsed = 0f;
float totalDuration = ClipScaledLength;
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;
foreach (float key in pending.Keys.ToList())
{
if (normalizedTime >= key)
{
pending[key]?.Invoke();
pending.Remove(key);
}
}
}
}
}
}