using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Spine.Unity;
using Cysharp.Threading.Tasks;
///
/// 角色动画基类,处理所有与动画相关的功能
///
public class MotionBase
{
public static List AttackMotionList = new List
{
MotionName.attack.ToString(),
MotionName.angerSkill.ToString(),
MotionName.passiveSkill.ToString(),
};
private Dictionary trackEntryCompleteDict = new Dictionary();
// 动画事件
public Action OnAttackAnimationComplete;
public Action OnHitAnimationComplete;
public Action onAnimationComplete;
#region 组件引用
protected SkeletonGraphic skeletonGraphic;
protected Spine.AnimationState spineAnimationState;
protected Spine.Skeleton skeleton;
#endregion
#region 动画设置
// 动画混合时间
protected float defaultMixDuration = 0f;
#endregion
private Spine.TrackEntry currentTrackEntry;
#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组件!");
}
}
public virtual void Release()
{
if (spineAnimationState != null)
{
spineAnimationState.Complete -= OnAnimationComplete;
spineAnimationState.ClearTracks();
spineAnimationState = null;
}
skeletonGraphic = null;
skeleton = null;
currentTrackEntry = null;
}
#endregion
#region 动画控制
///
/// 播放指定动画
///
/// 动画枚举
/// 是否循环
/// 动画播放完成回调
/// 动画轨道条目
public virtual Spine.TrackEntry PlayAnimation(MotionName motionName, bool loop, Action _onComplete = null)
{
if (spineAnimationState == null) return null;
// 如果当前动画未完成
if (currentTrackEntry != null && !currentTrackEntry.IsComplete)
{
if (trackEntryCompleteDict.TryGetValue(currentTrackEntry, out var __onComplete))
{
__onComplete?.Invoke();
trackEntryCompleteDict.Remove(currentTrackEntry);
}
currentTrackEntry = null;
}
// 直接使用 ToString() 而不是调用 GetAnimationName
currentTrackEntry = spineAnimationState.SetAnimation(0, motionName.ToString(), loop);
// 绑定回调
if (_onComplete != null && currentTrackEntry != null)
{
trackEntryCompleteDict[currentTrackEntry] = _onComplete;
}
return currentTrackEntry;
}
public Spine.TrackEntry PlaySkillAnimation(SkillConfig skillConfig, Action onComplete = null, Action onBeginPhaseEnd = null, Action onActivePhaseEnd = null)
{
if (skillConfig == null)
{
Debug.LogError("技能配置为空,无法播放技能动画");
return null;
}
return PlayAnimation(skillConfig.SkillMotionName, skillConfig.StartupFrames, skillConfig.ActiveFrames, skillConfig.LoopCount,
onComplete, onBeginPhaseEnd, onActivePhaseEnd);
}
public virtual Spine.TrackEntry PlayAnimation(
string animationName,
int loopBeginFrame,
int loopEndFrame,
int loopTimes,
Action _onComplete = null,
Action onBeginPhaseEnd = null, // 前摇结束回调
Action onActivePhaseEnd = null // 中摇结束回调
)
{
if (spineAnimationState == null || skeleton == null) return null;
var anim = skeleton.Data.FindAnimation(animationName);
if (anim == null) return null;
float fps = BattleConst.skillMotionFps;
float beginTime = loopBeginFrame / fps;
float endTime = loopEndFrame / fps;
currentTrackEntry = spineAnimationState.SetAnimation(0, anim, false);
int curLoop = 0;
bool finished = false;
bool beginPhaseTriggered = false;
Spine.Unity.UpdateBonesDelegate updateLocalHandler = null;
updateLocalHandler = (ISkeletonAnimation animated) =>
{
if (finished) return;
var entry = currentTrackEntry;
if (entry == null || entry.Animation != anim)
{
skeletonGraphic.UpdateLocal -= updateLocalHandler;
return;
}
// 前摇结束(只触发一次)
if (!beginPhaseTriggered && entry.TrackTime >= beginTime)
{
beginPhaseTriggered = true;
onBeginPhaseEnd?.Invoke();
}
// 中摇结束(每次到endTime都触发)
if (entry.TrackTime >= endTime)
{
onActivePhaseEnd?.Invoke();
curLoop++;
if (curLoop >= loopTimes)
{
finished = true;
skeletonGraphic.UpdateLocal -= updateLocalHandler;
_onComplete?.Invoke();
return;
}
entry.TrackTime = beginTime;
beginPhaseTriggered = false; // 重置,下一轮前摇可再次触发
}
};
skeletonGraphic.UpdateLocal += updateLocalHandler;
if (_onComplete != null && currentTrackEntry != null)
{
trackEntryCompleteDict[currentTrackEntry] = _onComplete;
}
return currentTrackEntry;
}
///
/// 设置动画事件监听
///
protected virtual void SetupAnimationHandlers()
{
if (spineAnimationState == null) return;
// 监听动画完成事件
spineAnimationState.Complete += OnAnimationComplete;
}
///
/// 动画完成事件处理
///
protected virtual void OnAnimationComplete(Spine.TrackEntry trackEntry)
{
string animation = trackEntry.Animation.Name;
// 攻击动画完成后恢复到待机状态
if (AttackMotionList.Contains(animation))
{
OnAttackAnimationComplete?.Invoke();
PlayAnimation(MotionName.idle, true);
}
// 受伤动画完成后恢复到待机状态 可能触发多次 因为有多段攻击的存在
else if (animation == MotionName.hit.ToString())
{
OnHitAnimationComplete?.Invoke();
PlayAnimation(MotionName.idle, true);
}
onAnimationComplete?.Invoke((MotionName)Enum.Parse(typeof(MotionName), animation));
// 只调用本次TrackEntry的回调
if (trackEntryCompleteDict.TryGetValue(trackEntry, out var cb))
{
cb?.Invoke();
trackEntryCompleteDict.Remove(trackEntry);
}
}
public void Test(string animationName, int beginFrame, int activeFrame, int endFrame, int activeFrameLoopCount)
{
// 要处理前摇beginFrame 后摇endFrame 中摇activeFrame
// 中摇是有多次的activeFrameLoopCount
var state = spineAnimationState;
var anim = skeleton.Data.FindAnimation(animationName);
// 设定你要循环的区间(单位:秒)
float loopStart = 0.5f;
float loopEnd = 1.2f;
// 播放动画
state.SetAnimation(0, anim, true);
// state.GetCurrent(0).TrackTime = loopStart;
int curFrame = 0;
skeletonGraphic.UpdateLocal += (skeletonAnim) =>
{
// if (curFrame == beginFrame)
// {
// OnBeginFrame?.Invoke();
// }
// else if (curFrame == activeFrame)
// {
// OnActiveFrame?.Invoke();
// }
// else if (curFrame == endFrame)
// {
// OnEndFrame?.Invoke();
// }
// var trackEntry = state.GetCurrent(0);
// if (trackEntry != null && trackEntry.Animation == anim)
// {
// if (trackEntry.TrackTime > loopEnd)
// {
// // 回到loopStart,实现区间循环
// trackEntry.TrackTime = loopStart;
// }
// }
};
}
public virtual void Run()
{
}
public virtual void Pause()
{
if (currentTrackEntry != null)
currentTrackEntry.TimeScale = 0f;
}
public virtual void Resume()
{
if (currentTrackEntry != null)
currentTrackEntry.TimeScale = 1f;
}
#endregion
}