From b5098dca7b3454da208d60d4944d132ca660bb77 Mon Sep 17 00:00:00 2001 From: yyl <yyl> Date: 星期二, 14 十月 2025 18:45:24 +0800 Subject: [PATCH] 125 战斗 子技能支持 --- Main/System/Battle/Motion/MotionBase.cs | 477 +++++++++++++++++++++++------------------------------------ 1 files changed, 187 insertions(+), 290 deletions(-) diff --git a/Main/System/Battle/Motion/MotionBase.cs b/Main/System/Battle/Motion/MotionBase.cs index e4434f2..4502828 100644 --- a/Main/System/Battle/Motion/MotionBase.cs +++ b/Main/System/Battle/Motion/MotionBase.cs @@ -1,17 +1,11 @@ using System; -using System.Collections; using System.Collections.Generic; using UnityEngine; using Spine.Unity; -using Cysharp.Threading.Tasks; -/// <summary> -/// 瑙掕壊鍔ㄧ敾鍩虹被锛屽鐞嗘墍鏈変笌鍔ㄧ敾鐩稿叧鐨勫姛鑳� -/// </summary> public class MotionBase { public static float MotionTimeScale = 1f; - public static List<string> AttackMotionList = new List<string> { MotionName.attack.ToString().ToLower(), @@ -19,429 +13,332 @@ MotionName.passiveSkill.ToString().ToLower(), }; - private Dictionary<Spine.TrackEntry, Action> trackEntryCompleteDict = new Dictionary<Spine.TrackEntry, Action>(); - - // 鍔ㄧ敾浜嬩欢 + private Dictionary<Spine.TrackEntry, Action> trackEntryCallbacks = new Dictionary<Spine.TrackEntry, Action>(); public Action OnAttackAnimationComplete; public Action OnHitAnimationComplete; - - private List<Action> runActionList = new List<Action>(); + private List<Action> runningActions = new List<Action>(); - #region 缁勪欢寮曠敤 - - protected SkeletonAnimation skeletonAnimation; - protected Spine.AnimationState spineAnimationState; + protected SkeletonAnimation skeletonAnim; + protected Spine.AnimationState animState; protected Spine.Skeleton skeleton; - - #endregion - - #region 鍔ㄧ敾璁剧疆 - - // 鍔ㄧ敾娣峰悎鏃堕棿 protected float defaultMixDuration = 0f; - - #endregion - - private Spine.TrackEntry currentTrackEntry; - - // 娈嬪奖鐢熸垚鍣� + private Spine.TrackEntry currentTrack; private SkeletonIllusionShadow illusionShadow; - + private bool playingSkillAnim = false; - #region 鍒濆鍖栨柟娉� - - /// <summary> - /// 鍒濆鍖栧姩鐢荤粍浠� - /// </summary> - /// <param name="skeletonGraphic">楠ㄩ鍔ㄧ敾缁勪欢</param> - public virtual void Init(SkeletonAnimation _skeletonAnimation) + public virtual void Init(SkeletonAnimation skelAnim) { - this.skeletonAnimation = _skeletonAnimation; - - if (skeletonAnimation != null) - { - spineAnimationState = skeletonAnimation.AnimationState; - spineAnimationState.TimeScale = MotionTimeScale; - skeletonAnimation.timeScale = MotionTimeScale; - - skeleton = skeletonAnimation.Skeleton; - - // 璁剧疆鍔ㄧ敾娣峰悎鏃堕棿 - if (spineAnimationState != null) - { - spineAnimationState.Data.DefaultMix = defaultMixDuration; - } - - // 鎾斁榛樿鍔ㄧ敾 - PlayAnimation(MotionName.idle, true); - - // 璁剧疆鍔ㄧ敾浜嬩欢鐩戝惉 - SetupAnimationHandlers(); - } - else + skeletonAnim = skelAnim; + if (skeletonAnim == null) { BattleDebug.LogError("缂哄皯SkeletonGraphic缁勪欢!"); + return; } - illusionShadow = _skeletonAnimation.gameObject.AddMissingComponent<SkeletonIllusionShadow>(); + animState = skeletonAnim.AnimationState; + animState.TimeScale = MotionTimeScale; + skeletonAnim.timeScale = MotionTimeScale; + skeleton = skeletonAnim.Skeleton; + + if (animState != null) + animState.Data.DefaultMix = defaultMixDuration; + + PlayAnimation(MotionName.idle, true); + SetupAnimationHandlers(); + + if (skelAnim.gameObject != null) + illusionShadow = skelAnim.gameObject.AddMissingComponent<SkeletonIllusionShadow>(); } public virtual void Release() { - trackEntryCompleteDict.Clear(); - if (spineAnimationState != null) + trackEntryCallbacks.Clear(); + if (animState != null) { - spineAnimationState.Complete -= OnAnimationComplete; - spineAnimationState.ClearTracks(); - spineAnimationState = null; + animState.Complete -= OnAnimationComplete; + animState.ClearTracks(); + animState = null; } - - skeletonAnimation = null; + skeletonAnim = null; skeleton = null; - currentTrackEntry = null; + currentTrack = null; + playingSkillAnim = false; } - #endregion - - #region 鍔ㄧ敾鎺у埗 - - /// <summary> - /// 鎾斁鎸囧畾鍔ㄧ敾 - /// </summary> - /// <param name="motionName">鍔ㄧ敾鏋氫妇</param> - /// <param name="loop">鏄惁寰幆</param> - /// <param name="_onComplete">鍔ㄧ敾鎾斁瀹屾垚鍥炶皟</param> - /// <returns>鍔ㄧ敾杞ㄩ亾鏉$洰</returns> - public virtual Spine.TrackEntry PlayAnimation(MotionName motionName, bool loop, Action _onComplete = null) + public virtual Spine.TrackEntry PlayAnimation(MotionName motionName, bool loop, Action onComplete = null) { - if (isPlaySkillAnimation) + if (playingSkillAnim || animState == null) return null; + + if (currentTrack != null && !currentTrack.IsComplete && trackEntryCallbacks.TryGetValue(currentTrack, out var prevCallback)) { - return null; + trackEntryCallbacks.Remove(currentTrack); + prevCallback?.Invoke(); + currentTrack = null; } - if (spineAnimationState == null) return null; + currentTrack = animState.SetAnimation(0, motionName.ToString(), loop); + if (onComplete != null && currentTrack != null) + trackEntryCallbacks[currentTrack] = onComplete; - // 濡傛灉褰撳墠鍔ㄧ敾鏈畬鎴� - 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; + return currentTrack; } - private void RunAction(Action _action) - { - _action?.Invoke(); - runActionList.Add(_action); - } + private void AddAction(Action action) => runningActions.Add(action); + private void RemoveAction(Action action) => runningActions.Remove(action); - private void RemoveRunAction(Action _action) + public Spine.TrackEntry PlaySkillAnimation(SkillConfig skillConfig, SkillBase skillBase, bool isSubSkill, Action onComplete = null) { - runActionList.Remove(_action); - } - - private bool isPlaySkillAnimation = false; - - public Spine.TrackEntry PlaySkillAnimation(SkillConfig skillConfig, SkillBase skillBase, Action _onComplete = null) - { - // 鍙傛暟鏍¢獙 if (skillConfig == null) { Debug.LogError("鎶�鑳介厤缃负绌猴紝鏃犳硶鎾斁鎶�鑳藉姩鐢�"); return null; } - if (spineAnimationState == null || skeleton == null) + if (animState == null || skeleton == null) { Debug.LogError("SkeletonGraphic鎴朅nimationState鏈垵濮嬪寲锛屾棤娉曟挱鏀炬妧鑳藉姩鐢�"); return null; } - - Spine.Animation anim = skeleton.Data.FindAnimation(skillConfig.SkillMotionName); - - if (null == anim) + if (string.IsNullOrEmpty(skillConfig.SkillMotionName)) { - 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) - { - Debug.LogError($"鎵句笉鍒板姩鐢�: {skillConfig.SkillMotionName}"); - skillBase.ForceFinished(); - // _onComplete?.Invoke(); + PlaySkillNoAnim(skillConfig, skillBase, onComplete, isSubSkill); return null; } - // 鍏抽敭甯у弬鏁� + Spine.Animation targetAnim = FindAnim(skillConfig.SkillMotionName); + if (targetAnim == null) + { + skillBase.ForceFinished(); + return null; + } + + return ExecuteSkillAnim(skillConfig, skillBase, onComplete, targetAnim, true, isSubSkill); + } + + private Spine.TrackEntry ExecuteSkillAnim(SkillConfig skillConfig, SkillBase skillBase, Action onComplete, + Spine.Animation targetAnim, bool hasAnim, bool isSubSkill) + { int loopCount = skillConfig.LoopCount; - int[] activeFrames = skillConfig.ActiveFrames; - int activeFrameCount = activeFrames.Length; + int[] activeFrames = skillConfig.ActiveFrames ?? new int[0]; + int frameCount = activeFrames.Length; float recoveryFrame = skillConfig.RecoveryFrames; - // 鎾斁鍔ㄧ敾 - var skillTrackEntry = spineAnimationState.SetAnimation(0, anim, false); - isPlaySkillAnimation = true; - currentTrackEntry = skillTrackEntry; + Spine.TrackEntry skillTrack = null; + if (hasAnim) + { + skillTrack = animState.SetAnimation(0, targetAnim, false); + currentTrack = skillTrack; + } + + playingSkillAnim = true; - // 浜嬩欢鐘舵�� - int curLoop = 0; - bool beginPhaseTriggered = false; - bool finalFrameStarted = false; - bool finalFrameEnded = false; - bool middleFrameStarted = false; - bool[] triggeredActiveFrame = new bool[activeFrameCount]; + int currentLoop = 0, triggerCount = 0, failCount = 0; + bool beginTriggered = false, finalStarted = false, finalEnded = false, middleStarted = false; + bool[] triggeredFrames = new bool[frameCount]; + float startTime = hasAnim ? 0 : Time.time; - // 鎶�鑳藉紑濮� skillBase.OnSkillStart(); - // 鍔ㄧ敾甯ф洿鏂板鐞� - int triggerMFEndCount = 0; - Action updateLocalHandler = null; - - int failCallbackTimes = 0; - - updateLocalHandler = () => + Action frameHandler = null; + frameHandler = () => { if (skillBase.IsFinished()) { - isPlaySkillAnimation = false; - RemoveRunAction(updateLocalHandler); + playingSkillAnim = false; + RemoveAction(frameHandler); return; } - float frame = (skillTrackEntry.TrackTime * skillTrackEntry.TimeScale * (float)BattleConst.skillMotionFps); + float currentFrame = 0f; + if (BattleConst.skillMotionFps > 0) + currentFrame = hasAnim ? (skillTrack.TrackTime * skillTrack.TimeScale * BattleConst.skillMotionFps) : ((Time.time - startTime) * MotionTimeScale * BattleConst.skillMotionFps); - if (currentTrackEntry != skillTrackEntry) + if (hasAnim) { - Debug.LogError("鎶�鑳藉姩鐢昏鎵撴柇锛屽己鍒剁粨鏉� " + skillConfig.SkillID + " last animation : " + (currentTrackEntry != null && currentTrackEntry.Animation != null ? currentTrackEntry.Animation.Name : "null")); + if (currentTrack != skillTrack) + Debug.LogError("鎶�鑳藉姩鐢昏鎵撴柇锛屽己鍒剁粨鏉� " + skillConfig.SkillID); + + if (skillTrack.TrackTime == 0) failCount++; + if (failCount > 100) + { + Debug.LogError("鎶�鑳藉姩鐢绘挱鏀惧け璐ワ紝寮哄埗缁撴潫 " + skillConfig.SkillID); + skillBase.ForceFinished(); + RemoveAction(frameHandler); + playingSkillAnim = false; + return; + } } - if (skillTrackEntry.TrackTime == 0) + if (!beginTriggered && currentFrame >= skillConfig.StartupFrames && currentLoop == 0) { - failCallbackTimes++; - } - - if (failCallbackTimes > 100) - { - Debug.LogError("鎶�鑳藉姩鐢绘挱鏀惧け璐ワ紝鍥炶皟寮傚父锛屽己鍒剁粨鏉� " + skillConfig.SkillID + " 瀵艰嚧閿欒鐨勫師鍥犳槸鎶�鑳藉抚閰嶇疆寰楀お涔呭鑷存妧鑳藉姩浣滅粨鏉熶簡浜嬩欢杩樻病缁撴潫"); - skillBase.ForceFinished(); - RemoveRunAction(updateLocalHandler); - isPlaySkillAnimation = true; - return; - } - - // 鍓嶆憞缁撴潫锛堝彧瑙﹀彂涓�娆★級 - if (!beginPhaseTriggered && frame >= skillConfig.StartupFrames && curLoop == 0) - { - beginPhaseTriggered = true; + beginTriggered = true; skillBase.OnStartSkillFrameEnd(); } - // 涓憞寮�濮嬶紙姣忚疆loop鐨勫紑濮嬶紝鍙Е鍙戜竴娆★級 - if (!middleFrameStarted && frame >= skillConfig.StartupFrames && curLoop <= loopCount) + if (!middleStarted && currentFrame >= skillConfig.StartupFrames && currentLoop <= loopCount) { - middleFrameStarted = true; - skillBase.OnMiddleFrameStart(curLoop); + middleStarted = true; + skillBase.OnMiddleFrameStart(currentLoop); } - // 澶氭鏀诲嚮甯цЕ鍙� - for (int hitIndex = 0; hitIndex < activeFrameCount; hitIndex++) + for (int i = 0; i < frameCount && i < triggeredFrames.Length; i++) { - float activeFrame = activeFrames[hitIndex]; - if (!triggeredActiveFrame[hitIndex] && frame >= activeFrame) + if (!triggeredFrames[i] && currentFrame >= activeFrames[i]) { - skillBase.OnMiddleFrameEnd(curLoop, triggerMFEndCount++); - triggeredActiveFrame[hitIndex] = true; + skillBase.OnMiddleFrameEnd(currentLoop, triggerCount++); + triggeredFrames[i] = true; } } - // 鍒ゆ柇鏄惁鎵�鏈塧ctiveFrame閮藉凡瑙﹀彂锛屽噯澶囪繘鍏ヤ笅涓�杞甽oop - bool allTriggered = true; - for (int i = 0; i < activeFrameCount; i++) - { - if (!triggeredActiveFrame[i]) - { - allTriggered = false; - break; - } - } + bool allTriggered = Array.TrueForAll(triggeredFrames, x => x); - // 杩涘叆涓嬩竴杞甽oop - if (allTriggered && curLoop < loopCount) + if (allTriggered && currentLoop < loopCount) { - curLoop++; - Array.Clear(triggeredActiveFrame, 0, activeFrameCount); - middleFrameStarted = false; + currentLoop++; + Array.Clear(triggeredFrames, 0, frameCount); + middleStarted = false; - if (curLoop < loopCount) + if (currentLoop < loopCount) { - // 閲嶆柊璁剧疆鍒扮涓�娆$殑涓憞鏃堕棿 - skillTrackEntry.TrackTime = skillConfig.StartupFrames / BattleConst.skillMotionFps; - beginPhaseTriggered = false; + if (BattleConst.skillMotionFps > 0) + { + if (hasAnim) + skillTrack.TrackTime = skillConfig.StartupFrames / BattleConst.skillMotionFps; + else + startTime = Time.time - (skillConfig.StartupFrames / BattleConst.skillMotionFps); + } + beginTriggered = false; } else { - finalFrameStarted = false; - finalFrameEnded = false; - // 鏀跺熬闃舵鐢卞悗缁�昏緫澶勭悊 + finalStarted = false; + finalEnded = false; } } - // 鏀跺熬闃舵锛歄nFinalFrameStart 鍜� OnFinalFrameEnd - if (curLoop >= loopCount) + if (currentLoop >= loopCount) { - if (!finalFrameStarted && frame >= recoveryFrame) + if (!finalStarted && currentFrame >= recoveryFrame) { - finalFrameStarted = true; + finalStarted = true; skillBase.OnFinalFrameStart(); } - if (finalFrameStarted && !finalFrameEnded && frame >= recoveryFrame) + if (finalStarted && !finalEnded && currentFrame >= recoveryFrame) { - finalFrameEnded = true; - isPlaySkillAnimation = false; - RemoveRunAction(updateLocalHandler); - _onComplete?.Invoke(); + finalEnded = true; + if (!isSubSkill) + { + playingSkillAnim = false; + } + RemoveAction(frameHandler); + onComplete?.Invoke(); skillBase.OnFinalFrameEnd(); } } }; - RunAction(updateLocalHandler); + AddAction(frameHandler); + return skillTrack; + } + + private Spine.Animation FindAnim(string animName) + { + if (string.IsNullOrEmpty(animName)) return null; - return skillTrackEntry; + Spine.Animation targetAnim = skeleton.Data.FindAnimation(animName); + if (targetAnim == null && skeleton.Data.Animations != null) + { + for (int i = 0; i < skeleton.Data.Animations.Count; i++) + { + var anim = skeleton.Data.Animations.Items[i]; + if (anim?.Name != null && anim.Name.ToLower() == animName.ToLower()) + { + targetAnim = anim; + break; + } + } + } + + if (targetAnim == null) + Debug.LogError($"鎵句笉鍒板姩鐢�: {animName}"); + + return targetAnim; } - - /// <summary> - /// 璁剧疆鍔ㄧ敾浜嬩欢鐩戝惉 - /// </summary> + private void PlaySkillNoAnim(SkillConfig skillConfig, SkillBase skillBase, Action onComplete, bool isSubSkill) => + ExecuteSkillAnim(skillConfig, skillBase, onComplete, null, false, isSubSkill); + protected virtual void SetupAnimationHandlers() { - if (spineAnimationState == null) return; - - // 鐩戝惉鍔ㄧ敾瀹屾垚浜嬩欢 - spineAnimationState.Complete += OnAnimationComplete; + if (animState != null) + animState.Complete += OnAnimationComplete; } - /// <summary> - /// 鍔ㄧ敾瀹屾垚浜嬩欢澶勭悊 - /// </summary> protected virtual void OnAnimationComplete(Spine.TrackEntry trackEntry) { - string animation = trackEntry.Animation.Name.ToLower(); + if (trackEntry?.Animation?.Name == null) return; + + string animName = trackEntry.Animation.Name.ToLower(); - // 鏀诲嚮鍔ㄧ敾瀹屾垚鍚庢仮澶嶅埌寰呮満鐘舵�� - if (AttackMotionList.Contains(animation)) + if (AttackMotionList.Contains(animName)) { OnAttackAnimationComplete?.Invoke(); PlayAnimation(MotionName.idle, true); } - // 鍙椾激鍔ㄧ敾瀹屾垚鍚庢仮澶嶅埌寰呮満鐘舵�� 鍙兘瑙﹀彂澶氭 鍥犱负鏈夊娈垫敾鍑荤殑瀛樺湪 - else if (animation == MotionName.hit.ToString().ToLower()) + else if (animName == MotionName.hit.ToString().ToLower()) { OnHitAnimationComplete?.Invoke(); PlayAnimation(MotionName.idle, true); } - // 鍙皟鐢ㄦ湰娆rackEntry鐨勫洖璋� - if (trackEntryCompleteDict.TryGetValue(trackEntry, out var cb)) + if (trackEntryCallbacks.TryGetValue(trackEntry, out var callback)) { - trackEntryCompleteDict.Remove(trackEntry); - cb?.Invoke(); + trackEntryCallbacks.Remove(trackEntry); + callback?.Invoke(); } } - - public virtual void Run() { - // #if UNITY_EDITOR - // List<int> removeIndex = new List<int>(); - // #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 - } + for (int i = runningActions.Count - 1; i >= 0; i--) + runningActions[i]?.Invoke(); - // #if UNITY_EDITOR - // // 绉婚櫎澶辫触鐨凙ction - // for (int i = 0; i < removeIndex.Count; i++) - // { - // runActionList.RemoveAt(removeIndex[i]); - // } - // #endif - illusionShadow.Run(); + illusionShadow?.Run(); } public virtual void Pause() { - spineAnimationState.TimeScale = 0f; - skeletonAnimation.timeScale = 0f; + if (animState != null) animState.TimeScale = 0f; + if (skeletonAnim != null) skeletonAnim.timeScale = 0f; } public virtual void Resume() { - spineAnimationState.TimeScale = MotionTimeScale; - skeletonAnimation.timeScale = MotionTimeScale; + if (animState != null) animState.TimeScale = MotionTimeScale; + if (skeletonAnim != null) skeletonAnim.timeScale = MotionTimeScale; } public void HaveRest() { - trackEntryCompleteDict.Clear(); - runActionList.Clear(); + trackEntryCallbacks.Clear(); + runningActions.Clear(); + playingSkillAnim = false; PlayAnimation(MotionName.idle, true); } public void SetSpeedRatio(float ratio) { MotionTimeScale = ratio; - spineAnimationState.TimeScale = ratio; - skeletonAnimation.timeScale = ratio; + if (animState != null) animState.TimeScale = ratio; + if (skeletonAnim != null) skeletonAnim.timeScale = ratio; } - public void ShowIllusionShadow(bool v) + public void ShowIllusionShadow(bool isVisible) { - illusionShadow.SetSkeletonAnimation(skeletonAnimation); - illusionShadow.Show(v); + if (illusionShadow != null) + { + illusionShadow.SetSkeletonAnimation(skeletonAnim); + illusionShadow.Show(isVisible); + } } - - #endregion - } \ No newline at end of file -- Gitblit v1.8.0