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
|
|
}
|