using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Spine.Unity;
using Cysharp.Threading.Tasks;
///
/// 角色动画基类,处理所有与动画相关的功能
///
public class MotionBase
{
public static float MotionTimeScale = 1f;
public static List AttackMotionList = new List
{
MotionName.attack.ToString().ToLower(),
MotionName.angerSkill.ToString().ToLower(),
MotionName.passiveSkill.ToString().ToLower(),
};
private Dictionary trackEntryCompleteDict = new Dictionary();
// 动画事件
public Action OnAttackAnimationComplete;
public Action OnHitAnimationComplete;
private List runActionList = new List();
#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;
spineAnimationState.TimeScale = MotionTimeScale;
skeletonGraphic.timeScale = MotionTimeScale;
skeleton = skeletonGraphic.Skeleton;
// 设置动画混合时间
if (spineAnimationState != null)
{
spineAnimationState.Data.DefaultMix = defaultMixDuration;
}
// 播放默认动画
PlayAnimation(MotionName.idle, true);
// 设置动画事件监听
SetupAnimationHandlers();
}
else
{
BattleDebug.LogError("缺少SkeletonGraphic组件!");
}
}
public virtual void Release()
{
trackEntryCompleteDict.Clear();
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))
{
trackEntryCompleteDict.Remove(currentTrackEntry);
__onComplete?.Invoke();
}
currentTrackEntry = null;
}
// 直接使用 ToString() 而不是调用 GetAnimationName
currentTrackEntry = spineAnimationState.SetAnimation(0, motionName.ToString(), loop);
// 绑定回调
if (_onComplete != null && currentTrackEntry != null)
{
trackEntryCompleteDict[currentTrackEntry] = _onComplete;
}
return currentTrackEntry;
}
private void RunAction(Action _action)
{
runActionList.Add(_action);
}
private void RemoveRunAction(Action _action)
{
runActionList.Remove(_action);
}
public Spine.TrackEntry PlaySkillAnimation(SkillConfig skillConfig, SkillBase skillBase, Action _onComplete = null)
{
// 参数校验
if (skillConfig == null)
{
BattleDebug.LogError("技能配置为空,无法播放技能动画");
return null;
}
if (spineAnimationState == null || skeleton == null)
{
BattleDebug.LogError("SkeletonGraphic或AnimationState未初始化,无法播放技能动画");
return null;
}
Spine.Animation anim = skeleton.Data.FindAnimation(skillConfig.SkillMotionName);
if (null == anim)
{
for (int i = 0; i < skeleton.Data.Animations.Count; i++)
{
var skeletonAnim = skeleton.Data.Animations.Items[i];
if (skeletonAnim.Name.ToLower() == skillConfig.SkillMotionName.ToLower())
{
anim = skeletonAnim;
// 找到动画
break;
}
}
}
// 获取动画
if (anim == null)
{
BattleDebug.LogError($"找不到动画: {skillConfig.SkillMotionName}");
_onComplete?.Invoke();
return null;
}
// 关键帧参数
int loopCount = skillConfig.LoopCount;
int[] activeFrames = skillConfig.ActiveFrames;
int activeFrameCount = activeFrames.Length;
float recoveryFrame = skillConfig.RecoveryFrames;
// 播放动画
var skillTrackEntry = spineAnimationState.SetAnimation(0, anim, false);
currentTrackEntry = skillTrackEntry;
// 事件状态
int curLoop = 0;
bool isFinish = false;
bool beginPhaseTriggered = false;
bool finalFrameStarted = false;
bool finalFrameEnded = false;
bool middleFrameStarted = false;
bool[] triggeredActiveFrame = new bool[activeFrameCount];
// 技能开始
skillBase.OnSkillStart();
// 动画帧更新处理
int triggerMFEndCount = 0;
Action updateLocalHandler = null;
updateLocalHandler = () =>
{
if (isFinish) return;
float frame = (skillTrackEntry.TrackTime * (float)BattleConst.skillMotionFps);
// 前摇结束(只触发一次)
if (!beginPhaseTriggered && frame >= skillConfig.StartupFrames && curLoop == 0)
{
beginPhaseTriggered = true;
skillBase.OnStartSkillFrameEnd();
}
// 中摇开始(每轮loop的开始,只触发一次)
if (!middleFrameStarted && frame >= skillConfig.StartupFrames && curLoop <= loopCount)
{
middleFrameStarted = true;
skillBase.OnMiddleFrameStart(curLoop);
}
// 多段攻击帧触发
for (int hitIndex = 0; hitIndex < activeFrameCount; hitIndex++)
{
float activeFrame = activeFrames[hitIndex];
if (!triggeredActiveFrame[hitIndex] && frame >= activeFrame)
{
skillBase.OnMiddleFrameEnd(curLoop, triggerMFEndCount++);
triggeredActiveFrame[hitIndex] = true;
}
}
// 判断是否所有activeFrame都已触发,准备进入下一轮loop
bool allTriggered = true;
for (int i = 0; i < activeFrameCount; i++)
{
if (!triggeredActiveFrame[i])
{
allTriggered = false;
break;
}
}
// 进入下一轮loop
if (allTriggered && curLoop < loopCount)
{
curLoop++;
Array.Clear(triggeredActiveFrame, 0, activeFrameCount);
middleFrameStarted = false;
if (curLoop < loopCount)
{
// 重新设置到第一次的中摇时间
skillTrackEntry.TrackTime = skillConfig.StartupFrames / BattleConst.skillMotionFps;
beginPhaseTriggered = false;
}
else
{
finalFrameStarted = false;
finalFrameEnded = false;
// 收尾阶段由后续逻辑处理
}
}
// 收尾阶段:OnFinalFrameStart 和 OnFinalFrameEnd
if (curLoop >= loopCount)
{
if (!finalFrameStarted && frame >= recoveryFrame)
{
finalFrameStarted = true;
skillBase.OnFinalFrameStart();
}
if (finalFrameStarted && !finalFrameEnded && frame >= recoveryFrame)
{
finalFrameEnded = true;
skillBase.OnFinalFrameEnd();
RemoveRunAction(updateLocalHandler);
isFinish = true;
}
}
};
if (_onComplete != null && currentTrackEntry != null)
{
trackEntryCompleteDict[currentTrackEntry] = _onComplete;
}
RunAction(updateLocalHandler);
return skillTrackEntry;
}
///
/// 设置动画事件监听
///
protected virtual void SetupAnimationHandlers()
{
if (spineAnimationState == null) return;
// 监听动画完成事件
spineAnimationState.Complete += OnAnimationComplete;
}
///
/// 动画完成事件处理
///
protected virtual void OnAnimationComplete(Spine.TrackEntry trackEntry)
{
string animation = trackEntry.Animation.Name.ToLower();
// 攻击动画完成后恢复到待机状态
if (AttackMotionList.Contains(animation))
{
OnAttackAnimationComplete?.Invoke();
PlayAnimation(MotionName.idle, true);
}
// 受伤动画完成后恢复到待机状态 可能触发多次 因为有多段攻击的存在
else if (animation == MotionName.hit.ToString().ToLower())
{
OnHitAnimationComplete?.Invoke();
PlayAnimation(MotionName.idle, true);
}
// 只调用本次TrackEntry的回调
if (trackEntryCompleteDict.TryGetValue(trackEntry, out var cb))
{
trackEntryCompleteDict.Remove(trackEntry);
cb?.Invoke();
}
}
public virtual void Run()
{
// #if UNITY_EDITOR
// List removeIndex = new List();
// #endif
for (int i = runActionList.Count - 1; i >= 0; i--)
{
// #if UNITY_EDITOR
// try
// {
// #endif
runActionList[i]?.Invoke();
// #if UNITY_EDITOR
// }
// catch (System.Exception ex)
// {
// removeIndex.Add(i);
// BattleDebug.LogError($"执行RunAction时发生异常: {ex.Message}\n{ex.StackTrace}");
// }
// #endif
}
// #if UNITY_EDITOR
// // 移除失败的Action
// for (int i = 0; i < removeIndex.Count; i++)
// {
// runActionList.RemoveAt(removeIndex[i]);
// }
// #endif
}
public virtual void Pause()
{
spineAnimationState.TimeScale = 0f;
skeletonGraphic.timeScale = 0f;
}
public virtual void Resume()
{
spineAnimationState.TimeScale = MotionTimeScale;
skeletonGraphic.timeScale = MotionTimeScale;
}
public void HaveRest()
{
trackEntryCompleteDict.Clear();
runActionList.Clear();
PlayAnimation(MotionName.idle, true);
}
#endregion
}