| using System; | 
| using System.Collections; | 
| using System.Collections.Generic; | 
| using UnityEngine; | 
| using Spine.Unity; | 
| using Cysharp.Threading.Tasks; | 
|   | 
| /// <summary> | 
| /// 角色动画基类,处理所有与动画相关的功能 | 
| /// </summary> | 
| public class MotionBase | 
| { | 
|     public class WaitingTask | 
|     { | 
|         public List<int> triggerFrame; | 
|         public MotionName motionName; | 
|         public int currentFrame; | 
|         public Action<MotionName, int> callback; | 
|   | 
|         public WaitingTask(List<int> triggerFrame, MotionName motionName, Action<MotionName, int> _callback) | 
|         { | 
|             this.triggerFrame = triggerFrame; | 
|             this.motionName = motionName; | 
|             this.currentFrame = 0; | 
|             this.callback = _callback; | 
|         } | 
|   | 
|         public bool IsFinished() | 
|         { | 
|             return triggerFrame.Count <= 0; | 
|         } | 
|   | 
|         public void Run() | 
|         { | 
|             if (triggerFrame.Count > 0) | 
|             { | 
|                 currentFrame ++; | 
|                  | 
|                 if (currentFrame >= triggerFrame[0]) | 
|                 { | 
|                     triggerFrame.RemoveAt(0); | 
|                     callback?.Invoke(motionName, currentFrame); | 
|                 } | 
|             } | 
|         } | 
|     } | 
|   | 
|     // 动画事件 | 
|     public Action OnAttackAnimationComplete; | 
|     public Action OnHitAnimationComplete; | 
|     public Action<int> OnAttackHitEvent; //trigger index | 
|   | 
|     #region 组件引用 | 
|      | 
|     protected SkeletonGraphic skeletonGraphic; | 
|     protected Spine.AnimationState spineAnimationState; | 
|     protected Spine.Skeleton skeleton; | 
|      | 
|     #endregion | 
|      | 
|     #region 动画设置 | 
|      | 
|     // 动画混合时间 | 
|     protected float defaultMixDuration = 0.2f; | 
|      | 
|     #endregion | 
|      | 
|     protected List<WaitingTask> waitingTaskList = new List<WaitingTask>(); | 
|     protected List<WaitingTask> removeList = new List<WaitingTask>(); | 
|   | 
|     #region 初始化方法 | 
|      | 
|     /// <summary> | 
|     /// 初始化动画组件 | 
|     /// </summary> | 
|     /// <param name="skeletonGraphic">骨骼动画组件</param> | 
|     public virtual void Init(SkeletonGraphic skeletonGraphic) | 
|     { | 
|         this.skeletonGraphic = skeletonGraphic; | 
|          | 
|         if (skeletonGraphic != null) | 
|         { | 
|             spineAnimationState = skeletonGraphic.AnimationState; | 
|             skeleton = skeletonGraphic.Skeleton; | 
|              | 
|             // 设置动画混合时间 | 
|             if (spineAnimationState != null) | 
|             { | 
|                 spineAnimationState.Data.DefaultMix = defaultMixDuration; | 
|             } | 
|              | 
|             // 播放默认动画 | 
|             PlayAnimation(MotionName.idle, true); | 
|              | 
|             // 设置动画事件监听 | 
|             SetupAnimationHandlers(); | 
|         } | 
|         else | 
|         { | 
|             Debug.LogError("缺少SkeletonGraphic组件!"); | 
|         } | 
|     } | 
|      | 
|     #endregion | 
|      | 
|     #region 动画控制 | 
|      | 
|     /// <summary> | 
|     /// 播放指定动画 | 
|     /// </summary> | 
|     /// <param name="motionName">动画枚举</param> | 
|     /// <param name="loop">是否循环</param> | 
|     /// <returns>动画轨道条目</returns> | 
|     public virtual Spine.TrackEntry PlayAnimation(MotionName motionName, bool loop) | 
|     { | 
|         if (spineAnimationState == null) return null; | 
|          | 
|         // 直接使用 ToString() 而不是调用 GetAnimationName | 
|         return spineAnimationState.SetAnimation(0, motionName.ToString(), loop); | 
|     } | 
|      | 
|     /// <summary> | 
|     /// 播放指定动画(使用字符串名称) | 
|     /// </summary> | 
|     /// <param name="animationName">动画名称</param> | 
|     /// <param name="loop">是否循环</param> | 
|     /// <returns>动画轨道条目</returns> | 
|     public virtual Spine.TrackEntry PlayAnimation(string animationName, bool loop) | 
|     { | 
|         if (spineAnimationState == null) return null; | 
|          | 
|         return spineAnimationState.SetAnimation(0, animationName, loop); | 
|     } | 
|      | 
|     /// <summary> | 
|     /// 添加动画到队列 | 
|     /// </summary> | 
|     /// <param name="motionName">动画枚举</param> | 
|     /// <param name="loop">是否循环</param> | 
|     /// <param name="delay">延迟时间</param> | 
|     /// <returns>动画轨道条目</returns> | 
|     public virtual Spine.TrackEntry AddAnimation(MotionName motionName, bool loop, float delay) | 
|     { | 
|         if (spineAnimationState == null) return null; | 
|          | 
|         // 直接使用 ToString() 而不是调用 GetAnimationName | 
|         return spineAnimationState.AddAnimation(0, motionName.ToString(), loop, delay); | 
|     } | 
|      | 
|     /// <summary> | 
|     /// 添加动画到队列(使用字符串名称) | 
|     /// </summary> | 
|     /// <param name="animationName">动画名称</param> | 
|     /// <param name="loop">是否循环</param> | 
|     /// <param name="delay">延迟时间</param> | 
|     /// <returns>动画轨道条目</returns> | 
|     public virtual Spine.TrackEntry AddAnimation(string animationName, bool loop, float delay) | 
|     { | 
|         if (spineAnimationState == null) return null; | 
|          | 
|         return spineAnimationState.AddAnimation(0, animationName, loop, delay); | 
|     } | 
|      | 
|     /// <summary> | 
|     /// 设置动画事件监听 | 
|     /// </summary> | 
|     protected virtual void SetupAnimationHandlers() | 
|     { | 
|         if (spineAnimationState == null) return; | 
|          | 
|         // 监听动画完成事件 | 
|         spineAnimationState.Complete += OnAnimationComplete; | 
|     } | 
|      | 
|     /// <summary> | 
|     /// 动画完成事件处理 | 
|     /// </summary> | 
|     protected virtual void OnAnimationComplete(Spine.TrackEntry trackEntry) | 
|     { | 
|         string animation = trackEntry.Animation.Name; | 
|          | 
|         // 攻击动画完成后恢复到待机状态 | 
|         if (animation == MotionName.atk1.ToString() ||  | 
|             animation == MotionName.atk2.ToString()) | 
|         { | 
|             OnAttackAnimationComplete?.Invoke(); | 
|             PlayAnimation(MotionName.idle, true); | 
|         } | 
|         // 受伤动画完成后恢复到待机状态 | 
|         else if (animation == MotionName.hit.ToString()) | 
|         { | 
|             OnHitAnimationComplete?.Invoke(); | 
|             PlayAnimation(MotionName.idle, true); | 
|         } | 
|     } | 
|      | 
|     /// <summary> | 
|     /// 动画事件处理 | 
|     /// </summary> | 
|     protected virtual void OnTriggerHitEvent(MotionName motionName, int hitIndex) | 
|     { | 
|         OnAttackHitEvent?.Invoke(hitIndex); | 
|     } | 
|   | 
|     //  拓展了播放动画方法,添加了触发帧的功能,在攻击动画中多段攻击的触发帧 | 
|     //  例如:攻击动画有三段,第一段攻击在第1帧触发,第二段攻击在第10帧触发,第三段攻击在第20帧触发 | 
|     //  那么triggerFrame就应该是[1, 10, 20] | 
|     public virtual void PlayAnimationEx(MotionName motionName, bool loop, List<int> triggerFrame = null) | 
|     { | 
|         PlayAnimation(motionName, loop); | 
|         if (triggerFrame != null && triggerFrame.Count >= 0) | 
|         { | 
|             AddWaitingTask(triggerFrame, motionName); | 
|         } | 
|     } | 
|   | 
|     protected void AddWaitingTask(List<int> triggerFrame, MotionName motionName) | 
|     { | 
|         if (triggerFrame != null && triggerFrame.Count > 0) | 
|         { | 
|             WaitingTask waitingTask = new WaitingTask(triggerFrame, motionName, OnTriggerHitEvent); | 
|             waitingTaskList.Add(waitingTask); | 
|         } | 
|     } | 
|   | 
|     public virtual void Run() | 
|     { | 
|         foreach (WaitingTask waitingTask in waitingTaskList) | 
|         { | 
|             waitingTask.Run(); | 
|   | 
|             if (waitingTask.IsFinished()) | 
|             { | 
|                 removeList.Add(waitingTask); | 
|             } | 
|         } | 
|   | 
|         foreach (WaitingTask waitingTask in removeList) | 
|         { | 
|             waitingTaskList.Remove(waitingTask); | 
|         } | 
|   | 
|         removeList.Clear(); | 
|     } | 
|     #endregion | 
|      | 
| } |