using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Spine.Unity;
using Cysharp.Threading.Tasks;
///
/// 角色动画基类,处理所有与动画相关的功能
///
public class MotionBase
{
public class WaitingTask
{
public List triggerFrame;
public MotionName motionName;
public int currentFrame;
public Action callback;
public WaitingTask(List triggerFrame, MotionName motionName, Action _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 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 waitingTaskList = new List();
protected List removeList = new List();
#region 初始化方法
///
/// 初始化动画组件
///
/// 骨骼动画组件
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 动画控制
///
/// 播放指定动画
///
/// 动画枚举
/// 是否循环
/// 动画轨道条目
public virtual Spine.TrackEntry PlayAnimation(MotionName motionName, bool loop)
{
if (spineAnimationState == null) return null;
// 直接使用 ToString() 而不是调用 GetAnimationName
return spineAnimationState.SetAnimation(0, motionName.ToString(), loop);
}
///
/// 播放指定动画(使用字符串名称)
///
/// 动画名称
/// 是否循环
/// 动画轨道条目
public virtual Spine.TrackEntry PlayAnimation(string animationName, bool loop)
{
if (spineAnimationState == null) return null;
return spineAnimationState.SetAnimation(0, animationName, loop);
}
///
/// 添加动画到队列
///
/// 动画枚举
/// 是否循环
/// 延迟时间
/// 动画轨道条目
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);
}
///
/// 添加动画到队列(使用字符串名称)
///
/// 动画名称
/// 是否循环
/// 延迟时间
/// 动画轨道条目
public virtual Spine.TrackEntry AddAnimation(string animationName, bool loop, float delay)
{
if (spineAnimationState == null) return null;
return spineAnimationState.AddAnimation(0, animationName, loop, delay);
}
///
/// 设置动画事件监听
///
protected virtual void SetupAnimationHandlers()
{
if (spineAnimationState == null) return;
// 监听动画完成事件
spineAnimationState.Complete += OnAnimationComplete;
}
///
/// 动画完成事件处理
///
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);
}
}
///
/// 动画事件处理
///
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 triggerFrame = null)
{
PlayAnimation(motionName, loop);
if (triggerFrame != null && triggerFrame.Count >= 0)
{
AddWaitingTask(triggerFrame, motionName);
}
}
protected void AddWaitingTask(List 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
}