2025-10-23 00:49:44 -04:00
|
|
|
|
using System;
|
2025-10-27 07:04:34 -04:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq;
|
2025-10-23 00:49:44 -04:00
|
|
|
|
using Continentis.MainGame.Character;
|
2026-03-20 11:56:50 -04:00
|
|
|
|
using Cysharp.Threading.Tasks;
|
2025-10-23 00:49:44 -04:00
|
|
|
|
using SLSFramework.General;
|
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Continentis.MainGame.Commands
|
|
|
|
|
|
{
|
|
|
|
|
|
public class Cmd_PlayAnimation : CommandBase
|
|
|
|
|
|
{
|
|
|
|
|
|
private readonly CombatCharacterViewBase characterView;
|
|
|
|
|
|
private readonly Animator animator;
|
2026-03-20 11:56:50 -04:00
|
|
|
|
private readonly bool waitForFinish;
|
|
|
|
|
|
private readonly float overrideDuration;
|
|
|
|
|
|
private readonly string animationName;
|
|
|
|
|
|
private readonly int layer;
|
2025-10-27 07:04:34 -04:00
|
|
|
|
|
|
|
|
|
|
private AnimationClip clip;
|
2026-03-20 11:56:50 -04:00
|
|
|
|
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)
|
2025-10-23 00:49:44 -04:00
|
|
|
|
{
|
|
|
|
|
|
this.characterView = characterView;
|
|
|
|
|
|
this.animator = characterView.animator;
|
2025-12-10 18:22:26 -05:00
|
|
|
|
this.animationName = animationName;
|
2025-10-23 00:49:44 -04:00
|
|
|
|
this.waitForFinish = waitForFinish;
|
|
|
|
|
|
this.overrideDuration = overrideDuration;
|
|
|
|
|
|
this.layer = layer;
|
2026-03-20 11:56:50 -04:00
|
|
|
|
characterView.animations.TryGetValue(animationName, out clip);
|
2025-10-27 07:04:34 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-20 11:56:50 -04:00
|
|
|
|
/// <summary>在动画的指定归一化时间点(0~1)执行 Action。</summary>
|
|
|
|
|
|
public Cmd_PlayAnimation AddAction(float normalizedTime, Action action)
|
2025-10-27 07:04:34 -04:00
|
|
|
|
{
|
2026-03-20 11:56:50 -04:00
|
|
|
|
animationActions[normalizedTime] = action;
|
2025-10-27 07:04:34 -04:00
|
|
|
|
return this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-20 11:56:50 -04:00
|
|
|
|
/// <summary>在动画的指定帧执行 Action。</summary>
|
2025-10-27 07:04:34 -04:00
|
|
|
|
public Cmd_PlayAnimation AddAction(int frame, Action action)
|
|
|
|
|
|
{
|
2026-03-20 11:56:50 -04:00
|
|
|
|
if (clip == null) return this;
|
|
|
|
|
|
float normalizedTime = frame / (clip.frameRate * clip.length);
|
|
|
|
|
|
return AddAction(normalizedTime, action);
|
2025-10-27 07:04:34 -04:00
|
|
|
|
}
|
2026-03-20 11:56:50 -04:00
|
|
|
|
|
|
|
|
|
|
/// <summary>在动画的指定归一化时间点执行带强类型参数的 Action,参数从 selfContext 读取。</summary>
|
|
|
|
|
|
public Cmd_PlayAnimation AddAction<T>(float normalizedTime, string selfContextKey, Action<T> action)
|
2025-10-27 07:04:34 -04:00
|
|
|
|
{
|
2026-03-20 11:56:50 -04:00
|
|
|
|
T param = selfContext.Get<T>(selfContextKey);
|
|
|
|
|
|
animationActions[normalizedTime] = () => action(param);
|
2025-10-27 07:04:34 -04:00
|
|
|
|
return this;
|
|
|
|
|
|
}
|
2026-03-20 11:56:50 -04:00
|
|
|
|
|
|
|
|
|
|
/// <summary>在动画的指定帧执行带强类型参数的 Action,参数从 selfContext 读取。</summary>
|
2025-10-27 07:04:34 -04:00
|
|
|
|
public Cmd_PlayAnimation AddAction<T>(int frame, string selfContextKey, Action<T> action)
|
|
|
|
|
|
{
|
2026-03-20 11:56:50 -04:00
|
|
|
|
if (clip == null) return this;
|
|
|
|
|
|
float normalizedTime = frame / (clip.frameRate * clip.length);
|
|
|
|
|
|
return AddAction(normalizedTime, selfContextKey, action);
|
2025-10-23 00:49:44 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-20 11:56:50 -04:00
|
|
|
|
protected override async UniTask ExecuteAsync(CommandContext outerContext)
|
2025-10-23 00:49:44 -04:00
|
|
|
|
{
|
2026-03-20 11:56:50 -04:00
|
|
|
|
if (animator == null || string.IsNullOrEmpty(animationName))
|
2025-10-23 00:49:44 -04:00
|
|
|
|
{
|
2026-03-20 11:56:50 -04:00
|
|
|
|
Debug.LogWarning("[Cmd_PlayAnimation] Animator 或动画名称为空。");
|
|
|
|
|
|
return;
|
2025-10-23 00:49:44 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-20 11:56:50 -04:00
|
|
|
|
// 确认播放目标动画,回退到 "Action"
|
|
|
|
|
|
string finalName = characterView.animations.ContainsKey(animationName) ? animationName : "Action";
|
|
|
|
|
|
|
|
|
|
|
|
if (!characterView.animations.TryGetValue(finalName, out clip))
|
2025-12-10 18:22:26 -05:00
|
|
|
|
{
|
2026-03-20 11:56:50 -04:00
|
|
|
|
Debug.LogWarning($"[Cmd_PlayAnimation] 找不到动画片段:{finalName}");
|
|
|
|
|
|
return;
|
2025-12-10 18:22:26 -05:00
|
|
|
|
}
|
2026-03-20 11:56:50 -04:00
|
|
|
|
|
|
|
|
|
|
characterView.animatorPlus2D.Play(clip);
|
|
|
|
|
|
|
|
|
|
|
|
// 帧轮询动画事件(fire-and-forget,不阻塞命令流)
|
|
|
|
|
|
if (animationActions.Count > 0)
|
|
|
|
|
|
PollAnimationActionsAsync().Forget();
|
|
|
|
|
|
|
|
|
|
|
|
if (waitForFinish)
|
2025-12-10 18:22:26 -05:00
|
|
|
|
{
|
2026-03-20 11:56:50 -04:00
|
|
|
|
float duration = overrideDuration >= 0f ? overrideDuration / animator.speed : ClipScaledLength;
|
|
|
|
|
|
await UniTask.Delay(TimeSpan.FromSeconds(duration));
|
2025-12-10 18:22:26 -05:00
|
|
|
|
}
|
2026-03-20 11:56:50 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private async UniTaskVoid PollAnimationActionsAsync()
|
|
|
|
|
|
{
|
|
|
|
|
|
float elapsed = 0f;
|
|
|
|
|
|
float totalDuration = ClipScaledLength;
|
|
|
|
|
|
var pending = new Dictionary<float, Action>(animationActions);
|
|
|
|
|
|
|
|
|
|
|
|
while (elapsed < totalDuration && pending.Count > 0)
|
2025-10-27 07:04:34 -04:00
|
|
|
|
{
|
2026-03-20 11:56:50 -04:00
|
|
|
|
await UniTask.Yield(PlayerLoopTiming.Update);
|
|
|
|
|
|
elapsed += Time.deltaTime;
|
|
|
|
|
|
float normalizedTime = animator.GetCurrentAnimatorStateInfo(layer).normalizedTime % 1f;
|
|
|
|
|
|
|
|
|
|
|
|
foreach (float key in pending.Keys.ToList())
|
2025-10-27 07:04:34 -04:00
|
|
|
|
{
|
2026-03-20 11:56:50 -04:00
|
|
|
|
if (normalizedTime >= key)
|
2025-10-27 07:04:34 -04:00
|
|
|
|
{
|
2026-03-20 11:56:50 -04:00
|
|
|
|
pending[key]?.Invoke();
|
|
|
|
|
|
pending.Remove(key);
|
2025-10-27 07:04:34 -04:00
|
|
|
|
}
|
2026-03-20 11:56:50 -04:00
|
|
|
|
}
|
2025-10-23 00:49:44 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-20 11:56:50 -04:00
|
|
|
|
}
|