Main/Component/UI/Common/SkeletonIllusionShadow.cs | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
Main/Component/UI/Common/SkeletonIllusionShadow.cs.meta | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
Main/System/Battle/BattleObject/BattleObject.cs | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
Main/System/Battle/Motion/MotionBase.cs | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
Main/Component/UI/Common/SkeletonIllusionShadow.cs
New file @@ -0,0 +1,128 @@ using Cysharp.Threading.Tasks; using System; using System.Collections; using System.Collections.Generic; using DG.Tweening; using Spine; using Spine.Unity; using UnityEngine; public class SkeletonIllusionShadow : MonoBehaviour { // 记录所有残影对象,便于统一销毁 private readonly List<GameObject> illusionShadows = new List<GameObject>(); private SkeletonAnimation skeletonAnimation; private bool createSwitch = false; private int curFrame = 0; private int frameInteral = 2; public void SetSkeletonAnimation(SkeletonAnimation _skeletonAnimation) { skeletonAnimation = _skeletonAnimation; } public void Show(bool v) { createSwitch = v; curFrame = 0; if (!v) { // 清理所有残影对象 for (int i = illusionShadows.Count - 1; i >= 0; i--) { var go = illusionShadows[i]; if (go != null) { DOTween.Kill(go, complete: false); if (Application.isPlaying) Destroy(go); else DestroyImmediate(go); } } illusionShadows.Clear(); } } public void Run() { if (createSwitch) { // 0.1秒创建一个残像 curFrame++; if (curFrame >= frameInteral) { curFrame -= frameInteral; CreateIllusionShadow(); } } } private void CreateIllusionShadow() { var src = skeletonAnimation.transform; // 保存源的世界变换 Vector3 worldPos = src.position; Quaternion worldRot = src.rotation; Vector3 worldScale = src.lossyScale; GameObject objTest = new GameObject("IllusionShadow"); illusionShadows.Add(objTest); objTest.transform.SetParent(null, false); objTest.transform.position = worldPos; objTest.transform.rotation = worldRot; objTest.transform.localScale = worldScale; // 当 parent == null 时 localScale == worldScale objTest.layer = LayerMask.NameToLayer("UI"); SkeletonAnimation sa = SkeletonAnimation.AddToGameObject(objTest, skeletonAnimation.skeletonDataAsset, false); TrackEntry trackEntry = skeletonAnimation.AnimationState.GetCurrent(0); TrackEntry playedEntry = sa.AnimationState.SetAnimation(0, trackEntry.Animation.Name, trackEntry.Loop); playedEntry.TrackTime = trackEntry.TrackTime; sa.timeScale = 0; sa.AnimationState.TimeScale = 0; playedEntry.TimeScale = 0; RendererAdjuster parent = skeletonAnimation.GetComponentInParent<RendererAdjuster>(); objTest.AddMissingComponent<RendererAdjuster>().SetSortingOrder(parent.sortingOrder); sa.skeleton.A = skeletonAnimation.skeleton.A; // 使用DoTween做alpha淡出,Tween与objTest绑定,便于统一Kill DOTween.To(() => sa.skeleton.A, x => { sa.skeleton.A = x; sa.LateUpdate(); }, 0f, 0.5f) .SetTarget(objTest); // 安全销毁,移除引用 _ = DestroyIllusionShadowAfterAsync(objTest, 0.5f); } // 用UniTask安全延迟销毁残影对象,多重安全检查 private async UniTaskVoid DestroyIllusionShadowAfterAsync(GameObject go, float delay) { try { await UniTask.Delay((int)(delay * 1000), cancellationToken: this.GetCancellationTokenOnDestroy()); } catch (OperationCanceledException) { // 对象已销毁,无需处理 return; } if (go == null) return; DOTween.Kill(go, complete: false); illusionShadows.Remove(go); if (Application.isPlaying) Destroy(go); else DestroyImmediate(go); } } Main/Component/UI/Common/SkeletonIllusionShadow.cs.meta
New file @@ -0,0 +1,11 @@ fileFormatVersion: 2 guid: 97920429032421a4081acb2dee82e387 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: Main/System/Battle/BattleObject/BattleObject.cs
@@ -288,13 +288,21 @@ teamHero.curHp = Math.Max(0, teamHero.curHp - (int)bounceHP); } const float pingpongTime = 0.2f; // 闪避开始 public virtual void OnDodgeBegin() { float pingpongTime = 0.2f; RectTransform rectTrans = heroRectTrans; var tween = rectTrans.DOAnchorPos(new Vector3(-30, 0, 0), pingpongTime) .SetEase(Ease.OutCubic); motionBase.ShowIllusionShadow(true); tween.onComplete += () => { motionBase.ShowIllusionShadow(false); }; battleField.battleTweenMgr.OnPlayTween(tween); } @@ -302,7 +310,6 @@ // 闪避结束 public virtual void OnDodgeEnd(Action _complete = null) { float pingpongTime = 0.2f; RectTransform rectTrans = heroRectTrans; var tween = rectTrans.DOAnchorPos(Vector3.zero, pingpongTime) Main/System/Battle/Motion/MotionBase.cs
@@ -43,6 +43,9 @@ #endregion private Spine.TrackEntry currentTrackEntry; // 残影生成器 private SkeletonIllusionShadow illusionShadow; #region 初始化方法 @@ -79,8 +82,8 @@ { BattleDebug.LogError("缺少SkeletonGraphic组件!"); } illusionShadow = _skeletonAnimation.gameObject.AddMissingComponent<SkeletonIllusionShadow>(); } public virtual void Release() @@ -377,33 +380,34 @@ public virtual void Run() { // #if UNITY_EDITOR // List<int> removeIndex = new List<int>(); // #endif // #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 // #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 // #if UNITY_EDITOR // // 移除失败的Action // for (int i = 0; i < removeIndex.Count; i++) // { // runActionList.RemoveAt(removeIndex[i]); // } // #endif illusionShadow.Run(); } public virtual void Pause() @@ -432,6 +436,12 @@ skeletonAnimation.timeScale = ratio; } public void ShowIllusionShadow(bool v) { illusionShadow.SetSkeletonAnimation(skeletonAnimation); illusionShadow.Show(v); } #endregion }