From 99a11d2bb19d74f6cc8584ac16838062af4fb301 Mon Sep 17 00:00:00 2001
From: yyl <yyl>
Date: 星期五, 03 四月 2026 11:24:07 +0800
Subject: [PATCH] webgl 优化

---
 Main/System/Battle/SkillEffect/NormalSkillEffect.cs               |    4 
 Main/ResModule/YooAssetService.cs                                 |   12 
 Main/System/Battle/SkillEffect/NoEffect.cs                        |    4 
 Main/Core/GameEngine/Launch/YooAssetInitTask.cs                   |    5 
 Main/System/Battle/Motion/MotionBase.cs                           |   40 +++
 Main/Utility/WaitForSecondsCache.cs.meta                          |   11 +
 Main/Component/UI/Decorate/Tweens/FillTween.cs                    |   12 
 Main/System/SpineUpdateManager.cs                                 |    4 
 Main/System/Battle/Buff/BattleObjectBuffMgr.cs                    |   39 ++
 Main/Utility/WaitForSecondsCache.cs                               |   28 ++
 Main/System/Equip/EquipTipWin.cs                                  |   12 
 Main/Component/UI/Effect/TimeMgr.cs                               |   17 
 Main/Utility/FrameEffect.cs                                       |    5 
 Main/Component/UI/Common/ButtonClickInterval.cs                   |    2 
 Main/System/Battle/SkillEffect/DotSkillEffect.cs                  |    2 
 Main/System/Battle/BattleEffectMgr.cs                             |   21 +
 Main/System/PhantasmPavilion/AvatarCell.cs                        |    5 
 Main/SDK/SDKUtils.cs                                              |    2 
 Main/System/Tip/ScrollTip.cs                                      |    2 
 Main/System/Hero/UIHeroController.cs                              |    7 
 Main/System/Sound/SoundPlayer.cs                                  |    2 
 Main/System/Battle/BattleManager.cs                               |   11 
 Main/System/Battle/BattleUtility.cs                               |    7 
 Main/System/Tip/MarqueeWin.cs                                     |    2 
 Main/Manager/UIManager.cs                                         |   35 +-
 Main/Component/UI/Effect/EllipseMask.cs                           |   11 
 Main/Component/UI/Decorate/Tweens/UIAlphaTween.cs                 |   12 
 Main/System/Battle/SkillEffect/SkillEffect.cs                     |   12 +
 Main/Component/UI/Decorate/Tweens/TweenEx.cs                      |    2 
 Main/System/Battle/BattleObject/HeroBattleObject.cs               |    9 
 Main/System/Battle/SkillEffect/BulletSkillEffect.cs               |    4 
 Main/Utility/UIHelper.cs                                          |    4 
 Main/System/Battle/RecordPlayer/RecordAction.cs                   |   26 ++
 Main/System/Battle/RecordPlayer/RecordPlayer.cs                   |   18 +
 Main/Core/NetworkPackage/Socket/ClientSocket.cs                   |   32 ++
 Main/System/Battle/Skill/SkillBase.cs                             |   86 ++++++-
 Main/System/Battle/BattleObject/BattleObjMgr.cs                   |   45 +++
 Main/System/Main/MainWin.cs                                       |    4 
 Main/System/Battle/BattleField/RecordActions/SkillRecordAction.cs |    8 
 Main/System/Battle/Sound/BattleSoundManager.cs                    |   53 +++-
 Main/Utility/UGUIEventListenerContainDrag.cs                      |   18 +
 41 files changed, 502 insertions(+), 133 deletions(-)

diff --git a/Main/Component/UI/Common/ButtonClickInterval.cs b/Main/Component/UI/Common/ButtonClickInterval.cs
index 4b44d99..33ece44 100644
--- a/Main/Component/UI/Common/ButtonClickInterval.cs
+++ b/Main/Component/UI/Common/ButtonClickInterval.cs
@@ -22,7 +22,7 @@
 
     IEnumerator DelayClick()
     {
-        yield return new WaitForSeconds(interval);
+        yield return WaitForSecondsCache.Get(interval);
         targetBtn.enabled = true;
     }
 }
diff --git a/Main/Component/UI/Decorate/Tweens/FillTween.cs b/Main/Component/UI/Decorate/Tweens/FillTween.cs
index 7b6d54c..efdf496 100644
--- a/Main/Component/UI/Decorate/Tweens/FillTween.cs
+++ b/Main/Component/UI/Decorate/Tweens/FillTween.cs
@@ -52,7 +52,7 @@
         if (this.gameObject.activeInHierarchy)
         {
             SetStartState();
-            StartCoroutine("Co_StartTween");
+            StartCoroutine(Co_StartTween());
         }
     }
 
@@ -72,7 +72,7 @@
                 SetStartState();
             }
 
-            StartCoroutine("Co_StartTween");
+            StartCoroutine(Co_StartTween());
         }
     }
 
@@ -84,7 +84,7 @@
         if (this.gameObject.activeInHierarchy)
         {
             SetStartState();
-            StartCoroutine("Co_StartTween");
+            StartCoroutine(Co_StartTween());
         }
     }
 
@@ -102,7 +102,7 @@
         if (trigger == Trigger.Start)
         {
             SetStartState();
-            StartCoroutine("Co_StartTween");
+            StartCoroutine(Co_StartTween());
         }
     }
 
@@ -111,7 +111,7 @@
         if (trigger == Trigger.Enable)
         {
             SetStartState();
-            StartCoroutine("Co_StartTween");
+            StartCoroutine(Co_StartTween());
         }
     }
 
@@ -152,7 +152,7 @@
 
         doTween = false;
         OnPrepare();
-        yield return new WaitForSeconds(delay);
+        yield return WaitForSecondsCache.Get(delay);
         curveLength = curve.keys[curve.keys.Length - 1].time - curve.keys[0].time;
         doTween = true;
         accumulatedTime = 0f;
diff --git a/Main/Component/UI/Decorate/Tweens/TweenEx.cs b/Main/Component/UI/Decorate/Tweens/TweenEx.cs
index d31e5c8..b858df6 100644
--- a/Main/Component/UI/Decorate/Tweens/TweenEx.cs
+++ b/Main/Component/UI/Decorate/Tweens/TweenEx.cs
@@ -144,7 +144,7 @@
             case DelayMode.Time:
                 if (delay > 0.001f)
                 {
-                    yield return new WaitForSeconds(delay);
+                    yield return WaitForSecondsCache.Get(delay);
                 }
                 break;
         }
diff --git a/Main/Component/UI/Decorate/Tweens/UIAlphaTween.cs b/Main/Component/UI/Decorate/Tweens/UIAlphaTween.cs
index 48d2ba6..920a2b0 100644
--- a/Main/Component/UI/Decorate/Tweens/UIAlphaTween.cs
+++ b/Main/Component/UI/Decorate/Tweens/UIAlphaTween.cs
@@ -53,7 +53,7 @@
         if (this.gameObject.activeInHierarchy)
         {
             SetStartState();
-            StartCoroutine("Co_StartTween");
+            StartCoroutine(Co_StartTween());
         }
     }
 
@@ -73,7 +73,7 @@
                 SetStartState();
             }
 
-            StartCoroutine("Co_StartTween");
+            StartCoroutine(Co_StartTween());
         }
     }
 
@@ -85,7 +85,7 @@
         if (this.gameObject.activeInHierarchy)
         {
             SetStartState();
-            StartCoroutine("Co_StartTween");
+            StartCoroutine(Co_StartTween());
         }
     }
 
@@ -103,7 +103,7 @@
         if (trigger == Trigger.Start)
         {
             SetStartState();
-            StartCoroutine("Co_StartTween");
+            StartCoroutine(Co_StartTween());
         }
     }
 
@@ -112,7 +112,7 @@
         if (trigger == Trigger.Enable)
         {
             SetStartState();
-            StartCoroutine("Co_StartTween");
+            StartCoroutine(Co_StartTween());
         }
     }
 
@@ -153,7 +153,7 @@
 
         doTween = false;
         OnPrepare();
-        yield return new WaitForSeconds(delay);
+        yield return WaitForSecondsCache.Get(delay);
         curveLength = curve.keys[curve.keys.Length - 1].time - curve.keys[0].time;
         doTween = true;
         accumulatedTime = 0f;
diff --git a/Main/Component/UI/Effect/EllipseMask.cs b/Main/Component/UI/Effect/EllipseMask.cs
index 8361294..de4b87d 100644
--- a/Main/Component/UI/Effect/EllipseMask.cs
+++ b/Main/Component/UI/Effect/EllipseMask.cs
@@ -23,6 +23,8 @@
     private Image m_MaskImage;
     private RectTransform m_RectTransform;
     private List<Graphic> m_MaskedChildren = new List<Graphic>();
+    // 缂撳瓨GetComponentsInChildren缁撴灉锛岄伩鍏嶆瘡娆nEnable鍒嗛厤鏁扮粍
+    private static List<Graphic> _graphicCacheList = new List<Graphic>();
 
     public Vector2 EllipseCenter
     {
@@ -228,10 +230,12 @@
         // 娓呴櫎涔嬪墠鐨勫垪琛�
         m_MaskedChildren.Clear();
         
-        // 鑾峰彇鎵�鏈夊瓙瀵硅薄鐨凣raphic缁勪欢
-        Graphic[] childrenGraphics = GetComponentsInChildren<Graphic>();
-        foreach (var graphic in childrenGraphics)
+        // 浣跨敤闈欐�佺紦瀛樺垪琛ㄩ伩鍏嶆瘡娆″垎閰嶆暟缁�
+        _graphicCacheList.Clear();
+        GetComponentsInChildren(false, _graphicCacheList);
+        for (int i = 0; i < _graphicCacheList.Count; i++)
         {
+            var graphic = _graphicCacheList[i];
             // 璺宠繃閬僵鏈韩
             if (graphic.gameObject == this.gameObject)
                 continue;
@@ -240,6 +244,7 @@
             CreateChildMaskMaterial(graphic);
             m_MaskedChildren.Add(graphic);
         }
+        _graphicCacheList.Clear();
     }
 
     /// <summary>
diff --git a/Main/Component/UI/Effect/TimeMgr.cs b/Main/Component/UI/Effect/TimeMgr.cs
index e1be090..eb3599e 100644
--- a/Main/Component/UI/Effect/TimeMgr.cs
+++ b/Main/Component/UI/Effect/TimeMgr.cs
@@ -59,7 +59,7 @@
         {
             Debug.Log(e.StackTrace);
         }
-        for (int i = 0; i < syntonyList.Count; i++)
+        for (int i = syntonyList.Count - 1; i >= 0; i--)
         {
             if ((TimeUtility.ServerNow - syntonyList[i].endTime).TotalSeconds > 0)
             {
@@ -68,21 +68,24 @@
                     syntonyList[i].callback();
                 }
                 var _type = syntonyList[i].type;
-                syntonyList.RemoveAt(i);
+                // Swap-remove閬垮厤O(n)绉讳綅
+                int last = syntonyList.Count - 1;
+                if (i < last)
+                    syntonyList[i] = syntonyList[last];
+                syntonyList.RemoveAt(last);
                 if (OnSyntonyEvent != null)
                 {
                     OnSyntonyEvent(_type);
                 }
-                i--;
             }
         }
         if (timeItems.Count > 0)
         {
-            timeItemList.RemoveRange(0, timeItemList.Count);
-            foreach (Component item in timeItems.Keys)
+            timeItemList.Clear();
+            foreach (var kv in timeItems)
             {
-                if (item == null) continue;
-                timeItemList.Add(timeItems[item]);
+                if (kv.Key == null) continue;
+                timeItemList.Add(kv.Value);
             }
             for (int i = 0; i < timeItemList.Count; i++)
             {
diff --git a/Main/Core/GameEngine/Launch/YooAssetInitTask.cs b/Main/Core/GameEngine/Launch/YooAssetInitTask.cs
index 7cce72c..8667c27 100644
--- a/Main/Core/GameEngine/Launch/YooAssetInitTask.cs
+++ b/Main/Core/GameEngine/Launch/YooAssetInitTask.cs
@@ -38,6 +38,7 @@
         {
             // Determine play mode based on AssetSource setting
             EPlayMode playMode;
+            YooAsset.IRemoteServices remoteServices = null;
             if (!AssetSource.isUseAssetBundle)
             {
                 // Editor 涓嶄娇鐢� AB 妯″紡锛欵ditorSimulateMode 鐩存帴璇� AssetDatabase
@@ -48,6 +49,8 @@
 #if UNITY_WEBGL
                 // WebGL 骞冲彴锛堝惈 Editor 涓嬪垏鍒� WebGL target锛夛細BuildInFileSystem 涓嶆敮鎸� WebGL锛屽繀椤荤敤 WebPlayMode
                 playMode = EPlayMode.WebPlayMode;
+                // 杩滅▼妯″紡锛氳祫婧愪笉闅忓寘锛屼粠 HTTP 鏈嶅姟鍣ㄥ姞杞�
+                remoteServices = WebGLRemoteConfig.CreateRemoteServices();
 #elif UNITY_EDITOR
                 // Editor 闈� WebGL target + AB 妯″紡锛氫粠鏈湴 StreamingAssets 鍔犺浇宸叉瀯寤虹殑 AB
                 playMode = EPlayMode.OfflinePlayMode;
@@ -58,7 +61,7 @@
             }
 
             // Initialize YooAssetService
-            await YooAssetService.Instance.InitializeAsync(playMode);
+            await YooAssetService.Instance.InitializeAsync(playMode, remoteServices);
 
             // Register as IYooAssetBridge for Launch assembly cross-assembly access
             YooAssetBridgeHolder.Register(YooAssetService.Instance);
diff --git a/Main/Core/NetworkPackage/Socket/ClientSocket.cs b/Main/Core/NetworkPackage/Socket/ClientSocket.cs
index 12a16c2..ae90b8d 100644
--- a/Main/Core/NetworkPackage/Socket/ClientSocket.cs
+++ b/Main/Core/NetworkPackage/Socket/ClientSocket.cs
@@ -32,6 +32,7 @@
     // WebSocket 瀹炵幇锛圵ebGL骞冲彴锛�
     WebSocket webSocket;
     public WebSocket socket { get { return webSocket; } }
+    private byte[] fragmentBytes; // TCP-to-WS缃戝叧鎸塗CP缂撳啿鍖烘媶鍖咃紝闇�瑕佽法娑堟伅閲嶇粍
 #endif
 
     public Action OnDisconnected;
@@ -489,8 +490,16 @@
         {
             getBytesTotal += data.Length;
             
-            // WebSocket鏄秷鎭ā寮忥紝姣忔鏀跺埌瀹屾暣鍖咃紝鐩存帴澶勭悊
             byte[] fixBytes = data;
+            // TCP-to-WS缃戝叧鎸塗CP缂撳啿鍖哄ぇ灏忔媶鍒嗭紝闇�璺ㄦ秷鎭噸缁勶紙涓嶵CP ReadInfo閫昏緫涓�鑷达級
+            if (fragmentBytes != null && fragmentBytes.Length > 0)
+            {
+                fixBytes = new byte[fragmentBytes.Length + data.Length];
+                Array.Copy(fragmentBytes, 0, fixBytes, 0, fragmentBytes.Length);
+                Array.Copy(data, 0, fixBytes, fragmentBytes.Length, data.Length);
+            }
+            fragmentBytes = null;
+
             int vReadIndex = 0;
             byte[] vPackBytes;
             int vLeavingLeng = 0;
@@ -503,14 +512,30 @@
                 vLeavingLeng = vTotalLeng - vReadIndex;
                 if (vLeavingLeng < 6)
                 {
-                    Debug.LogError($"[ClientSocket-WebSocket] 鍖呮暟鎹笉瓒�: {vLeavingLeng} bytes");
+                    fragmentBytes = new byte[vLeavingLeng];
+                    Array.Copy(fixBytes, vReadIndex, fragmentBytes, 0, vLeavingLeng);
+                    break;
+                }
+                
+                // 鏍¢獙FFCC鍖呭ご锛岄槻姝㈡暟鎹敊浣�
+                if (fixBytes[vReadIndex] != 0xFF || fixBytes[vReadIndex + 1] != 0xCC)
+                {
+                    Debug.LogError($"[ClientSocket-WebSocket] FFCC鍖呭ご寮傚父: {fixBytes[vReadIndex]:X2} {fixBytes[vReadIndex + 1]:X2}, 涓㈠純鍓╀綑 {vLeavingLeng} 瀛楄妭");
+                    fragmentBytes = null;
                     break;
                 }
                 
                 vBodyLeng = BitConverter.ToInt32(fixBytes, vReadIndex + 2);
+                if (vBodyLeng <= 0)
+                {
+                    Debug.LogError($"[ClientSocket-WebSocket] 鍖呬綋闀垮害闈炴硶: {vBodyLeng}, 涓㈠純");
+                    fragmentBytes = null;
+                    break;
+                }
                 if (vBodyLeng > vLeavingLeng - 6)
                 {
-                    Debug.LogError($"[ClientSocket-WebSocket] 鍖呴暱搴︿笉鍖归厤: 澹版槑 {vBodyLeng + 6}, 瀹為檯 {vLeavingLeng}");
+                    fragmentBytes = new byte[vLeavingLeng];
+                    Array.Copy(fixBytes, vReadIndex, fragmentBytes, 0, vLeavingLeng);
                     break;
                 }
                 
@@ -573,6 +598,7 @@
     public async void CloseConnect()
     {
         Debug.Log("[ClientSocket-WebSocket] ==== CloseConnect");
+        fragmentBytes = null;
         
         if (webSocket != null)
         {
diff --git a/Main/Manager/UIManager.cs b/Main/Manager/UIManager.cs
index 12a2402..23889f4 100644
--- a/Main/Manager/UIManager.cs
+++ b/Main/Manager/UIManager.cs
@@ -48,6 +48,10 @@
     // 褰撳墠鏈�楂樼殑鎺掑簭椤哄簭
     private int currentHighestSortingOrder = 0;
     
+    // 鎺掑簭缂撳瓨锛岄伩鍏嶆瘡娆pdateUISortingOrder鍒嗛厤
+    private List<UIBase> _sortTempList = new List<UIBase>(32);
+    private Dictionary<UIBase, int> _sortOrderDict = new Dictionary<UIBase, int>(32);
+    
     // 褰撳墠鍥炲悎鏁帮紝鐢ㄤ簬璁板綍UI鐨勪娇鐢ㄦ儏鍐�
     private int currentRound = 0;
     
@@ -923,34 +927,35 @@
         // 閲嶇疆褰撳墠鏈�楂樻帓搴忛『搴�
         currentHighestSortingOrder = 0;
         
-        // 閬嶅巻UI鏍堬紝璁剧疆鎺掑簭椤哄簭
-        UIBase[] uiArray = new UIBase[uiStack.Count];
-        uiStack.CopyTo(uiArray, 0);
-
-        // WebGL/IL2CPP 涓嬭 Destroy 鐨勫璞¤闂换浣曞睘鎬ч兘浼� NullReferenceException锛�
-        // 蹇呴』鍦ㄤ娇鐢ㄥ墠杩囨护鎺夛紙Unity 浼� null锛欳# 寮曠敤闈� null 浣� == null 涓� true锛�
-        uiArray = System.Array.FindAll(uiArray, ui => ui != null);
+        // 澶嶇敤涓存椂鍒楄〃锛岄伩鍏嶆瘡娆″垎閰嶆暟缁�
+        _sortTempList.Clear();
+        _sortOrderDict.Clear();
         
-        // 鍏堟寜鐓ILayer杩涜鎺掑簭锛岀劧鍚庡啀鎸夌収鏍堥『搴忔帓搴�
-        Dictionary<UIBase, int> uiOrderDict = new Dictionary<UIBase, int>();
-        for (int i = 0; i < uiArray.Length; i++)
+        int index = 0;
+        foreach (var ui in uiStack)
         {
-            uiOrderDict[uiArray[i]] = i;
+            if (ui != null)
+            {
+                _sortTempList.Add(ui);
+                _sortOrderDict[ui] = index;
+                index++;
+            }
         }
 
-        Array.Sort(uiArray, (a, b) =>
+        _sortTempList.Sort((a, b) =>
         {
             if (a == null || b == null) return 0;
             int layerCompare = a.uiLayer.CompareTo(b.uiLayer);
             if (layerCompare != 0)
                 return layerCompare;
 
-            return uiOrderDict[b].CompareTo(uiOrderDict[a]);
+            return _sortOrderDict[b].CompareTo(_sortOrderDict[a]);
         });
 
-        // 閬嶅巻鎺掑簭鍚庣殑UI鏁扮粍锛岃缃帓搴忛『搴�
-        foreach (var ui in uiArray)
+        // 閬嶅巻鎺掑簭鍚庣殑UI鍒楄〃锛岃缃帓搴忛『搴�
+        for (int i = 0; i < _sortTempList.Count; i++)
         {
+            var ui = _sortTempList[i];
             if (ui == null) continue;
             // 鑾峰彇鍩虹鎺掑簭椤哄簭
             int baseSortingOrder = GetBaseSortingOrderForLayer(ui.uiLayer);
diff --git a/Main/ResModule/YooAssetService.cs b/Main/ResModule/YooAssetService.cs
index 81bcaca..b501d7f 100644
--- a/Main/ResModule/YooAssetService.cs
+++ b/Main/ResModule/YooAssetService.cs
@@ -266,13 +266,19 @@
                     webParams.WebServerFileSystemParameters = TiktokFileSystemCreater
                         .CreateFileSystemParameters(packageRoot, remoteServices);
 #else
-                    webParams.WebServerFileSystemParameters = FileSystemParameters
-                        .CreateDefaultWebServerFileSystemParameters();
                     if (remoteServices != null)
                     {
-                        webParams.WebRemoteFileSystemParameters = FileSystemParameters
+                        // 杩滅▼妯″紡锛圠ocalCDN/RemoteCDN锛夛細璧勬簮涓嶅湪 StreamingAssets锛�
+                        // 璺宠繃 WebServerFileSystem锛屽彧鐢� WebRemoteFileSystem 浠� HTTP 鏈嶅姟鍣ㄥ姞杞�
+                        webParams.WebServerFileSystemParameters = FileSystemParameters
                             .CreateDefaultWebRemoteFileSystemParameters(remoteServices);
                     }
+                    else
+                    {
+                        // Local 妯″紡锛氳祫婧愬湪 StreamingAssets锛岀敤 WebServerFileSystem
+                        webParams.WebServerFileSystemParameters = FileSystemParameters
+                            .CreateDefaultWebServerFileSystemParameters();
+                    }
 #endif
                     return webParams;
                 }
diff --git a/Main/SDK/SDKUtils.cs b/Main/SDK/SDKUtils.cs
index b43896b..1390f4b 100644
--- a/Main/SDK/SDKUtils.cs
+++ b/Main/SDK/SDKUtils.cs
@@ -239,7 +239,7 @@
                 }
             }
 
-            yield return new WaitForSeconds(1f);
+            yield return WaitForSecondsCache.Wait1;
             // yield return WaitingForSecondConst.WaitMS1000;
         }
     }
diff --git a/Main/System/Battle/BattleEffectMgr.cs b/Main/System/Battle/BattleEffectMgr.cs
index a015f53..71f9ed4 100644
--- a/Main/System/Battle/BattleEffectMgr.cs
+++ b/Main/System/Battle/BattleEffectMgr.cs
@@ -10,6 +10,9 @@
 
     private Dictionary<int, List<BattleEffectPlayer>> effectDict = new Dictionary<int, List<BattleEffectPlayer>>();
 
+    // 缂撳瓨Run()鐨勪复鏃跺垪琛紝閬垮厤姣忓抚鍒嗛厤
+    private List<BattleEffectPlayer> _runList = new List<BattleEffectPlayer>();
+
     public void Init(BattleField _battleField)
     {
         Reload(_battleField);
@@ -23,16 +26,16 @@
 
     public void Run()
     {
-        List<BattleEffectPlayer> runList = new List<BattleEffectPlayer>();
+        _runList.Clear();
 
         foreach (KeyValuePair<int, List<BattleEffectPlayer>> kvPair in effectDict)
         {
-            runList.AddRange(kvPair.Value);
+            _runList.AddRange(kvPair.Value);
         }
 
-        for (int i = runList.Count - 1; i >= 0; i--)
+        for (int i = _runList.Count - 1; i >= 0; i--)
         {
-            BattleEffectPlayer effectPlayer = runList[i];
+            BattleEffectPlayer effectPlayer = _runList[i];
             if (effectPlayer != null)
             {
                 effectPlayer.Run();
@@ -169,11 +172,13 @@
 
     public void Release()
     {
-        List<int> fKeys = effectDict.Keys.ToList();
-
-        for (int i = 0; i < fKeys.Count; i++)
+        // 蹇呴』鍏堝揩鐓ey鍒楄〃锛屽洜涓篋estroyImmediate浼氬悓姝ヨЕ鍙慜nEffectDestroy淇敼effectDict
+        _runList.Clear();
+        var keys = new List<int>(effectDict.Keys);
+        for (int k = 0; k < keys.Count; k++)
         {
-            List<BattleEffectPlayer> effectPlayers = effectDict[fKeys[i]];
+            if (!effectDict.TryGetValue(keys[k], out var effectPlayers))
+                continue;
             while (effectPlayers.Count > 0)
             {
                 var effectPlayer = effectPlayers[0];
diff --git a/Main/System/Battle/BattleField/RecordActions/SkillRecordAction.cs b/Main/System/Battle/BattleField/RecordActions/SkillRecordAction.cs
index d70c82b..23b69ab 100644
--- a/Main/System/Battle/BattleField/RecordActions/SkillRecordAction.cs
+++ b/Main/System/Battle/BattleField/RecordActions/SkillRecordAction.cs
@@ -13,11 +13,11 @@
 
 public class SkillRecordAction : RecordAction
 {
-#if UNITY_EDITOR
+// #if UNITY_EDITOR
 	public 
-#else
-	protected 
-#endif 
+// #else
+// 	protected 
+// #endif 
 
 
 		SkillBase skillBase;
diff --git a/Main/System/Battle/BattleManager.cs b/Main/System/Battle/BattleManager.cs
index f32cf2d..3835e4c 100644
--- a/Main/System/Battle/BattleManager.cs
+++ b/Main/System/Battle/BattleManager.cs
@@ -12,6 +12,8 @@
 
     //  鍚屾椂鍙兘鏈変竴鍦烘垬鏂楀湪杩涜 guid, battlefield
     protected Dictionary<string, BattleField> battleFields = new Dictionary<string, BattleField>();
+    // 缂撳瓨key鍒楄〃锛岄伩鍏峈un()姣忓抚鍒嗛厤
+    private List<string> _runKeysCache = new List<string>();
 
     public float[] speedGear; //鎴樻枟鍊嶆暟瀵瑰簲鐨勫疄闄呴�熺巼
     public int speedIndex
@@ -631,10 +633,13 @@
     public void Run()
     {
 
-        List<string> keys = new List<string>(battleFields.Keys);
-        for (int i = keys.Count - 1; i >= 0; i--)
+        _runKeysCache.Clear();
+        foreach (var key in battleFields.Keys)
+            _runKeysCache.Add(key);
+        for (int i = _runKeysCache.Count - 1; i >= 0; i--)
         {
-            var battleField = battleFields[keys[i]];
+            if (!battleFields.TryGetValue(_runKeysCache[i], out var battleField))
+                continue;
             try
             {
                 battleField?.Run();
diff --git a/Main/System/Battle/BattleObject/BattleObjMgr.cs b/Main/System/Battle/BattleObject/BattleObjMgr.cs
index 9fdadf0..9b301df 100644
--- a/Main/System/Battle/BattleObject/BattleObjMgr.cs
+++ b/Main/System/Battle/BattleObject/BattleObjMgr.cs
@@ -8,8 +8,39 @@
 public class BattleObjMgr
 {
     //  姝讳骸涓嶅彲浠ュ皢BattleObject绉诲嚭瀛楀吀/鍒楄〃
-    public List<BattleObject> redCampList => new List<BattleObject>(redCampDict.Values);
-    public List<BattleObject> blueCampList => new List<BattleObject>(blueCampDict.Values);
+    // 缂撳瓨闃佃惀鍒楄〃锛屼粎鍦ㄥ瓧鍏稿彉鏇存椂閲嶅缓锛岄伩鍏嶆瘡娆″睘鎬ц闂垎閰嶆柊List
+    private List<BattleObject> _redCampListCache;
+    private List<BattleObject> _blueCampListCache;
+    private bool _redCampDirty = true;
+    private bool _blueCampDirty = true;
+    public List<BattleObject> redCampList
+    {
+        get
+        {
+            if (_redCampDirty || _redCampListCache == null)
+            {
+                if (_redCampListCache == null) _redCampListCache = new List<BattleObject>(redCampDict.Count);
+                else _redCampListCache.Clear();
+                foreach (var kv in redCampDict) _redCampListCache.Add(kv.Value);
+                _redCampDirty = false;
+            }
+            return _redCampListCache;
+        }
+    }
+    public List<BattleObject> blueCampList
+    {
+        get
+        {
+            if (_blueCampDirty || _blueCampListCache == null)
+            {
+                if (_blueCampListCache == null) _blueCampListCache = new List<BattleObject>(blueCampDict.Count);
+                else _blueCampListCache.Clear();
+                foreach (var kv in blueCampDict) _blueCampListCache.Add(kv.Value);
+                _blueCampDirty = false;
+            }
+            return _blueCampListCache;
+        }
+    }
     private Dictionary<int, BattleObject> redCampDict = new Dictionary<int, BattleObject>();
     private Dictionary<int, BattleObject> blueCampDict = new Dictionary<int, BattleObject>();
 
@@ -40,6 +71,12 @@
         await CreateTeam(posNodeList, campDict, teamBase, _camp, active);
     }
 
+    private void MarkCampDirty(Dictionary<int, BattleObject> campDict)
+    {
+        if (campDict == redCampDict) _redCampDirty = true;
+        else if (campDict == blueCampDict) _blueCampDirty = true;
+    }
+
     protected async UniTask CreateTeam(List<GameObject> posNodeList, Dictionary<int, BattleObject> campDict, TeamBase teamBase, BattleCamp _Camp, bool active)
     {
         DestroyTeam(campDict);
@@ -61,6 +98,7 @@
                 battleObj.SetSpeedRatio(battleField.speedRatio);
             }
         }
+        MarkCampDirty(campDict);
 
         if (teamBase.teamMingge != null)
         {
@@ -163,10 +201,12 @@
                 if (battleObj.Camp == BattleCamp.Red)
                 {
                     redCampDict.Remove(battleObj.GetPositionNum());
+                    _redCampDirty = true;
                 }
                 else
                 {
                     blueCampDict.Remove(battleObj.GetPositionNum());
+                    _blueCampDirty = true;
                 }
                 allBattleObjDict.Remove((int)objID);
                 BattleObjectFactory.DestroyBattleObject((int)objID, battleObj);
@@ -187,6 +227,7 @@
             }
         }
         campDict.Clear();
+        MarkCampDirty(campDict);
     }
 
     //  绌洪棽鐘舵��
diff --git a/Main/System/Battle/BattleObject/HeroBattleObject.cs b/Main/System/Battle/BattleObject/HeroBattleObject.cs
index 7db9465..150d8ff 100644
--- a/Main/System/Battle/BattleObject/HeroBattleObject.cs
+++ b/Main/System/Battle/BattleObject/HeroBattleObject.cs
@@ -201,6 +201,15 @@
     {
         return motionBase.CanCastSkill(skillSkinConfig);
     }
+
+    /// <summary>
+    /// 寮哄埗閲嶇疆MotionBase鐨勬妧鑳藉姩鐢荤姸鎬�
+    /// 鐢ㄤ簬鎶�鑳紽orceFinish鍚庨槻姝layingSkillWithAnim鍗′綇鍚庣画鎶�鑳�
+    /// </summary>
+    public void ForceResetMotionSkillState()
+    {
+        motionBase?.ForceResetSkillState();
+    }
     
     public override SkeletonAnimation GetSkeletonAnimation()
     {
diff --git a/Main/System/Battle/BattleUtility.cs b/Main/System/Battle/BattleUtility.cs
index 91a4f31..e598cde 100644
--- a/Main/System/Battle/BattleUtility.cs
+++ b/Main/System/Battle/BattleUtility.cs
@@ -161,6 +161,11 @@
 
     public static int GetMainTargetPositionNum(SkillBase skillBase, BattleObject caster, List<HB427_tagSCUseSkill.tagSCUseSkillHurt> targetList, SkillConfig skillConfig)
     {
+        return GetMainTargetPositionNum(skillBase, caster, (IReadOnlyList<HB427_tagSCUseSkill.tagSCUseSkillHurt>)targetList, skillConfig);
+    }
+
+    public static int GetMainTargetPositionNum(SkillBase skillBase, BattleObject caster, IReadOnlyList<HB427_tagSCUseSkill.tagSCUseSkillHurt> targetList, SkillConfig skillConfig)
+    {
         int returnIndex = 0;
         //  鏍规嵁鏁屾柟琛�閲忛樀钀� 瀛樻椿浜烘暟鏉ラ�夋嫨
         BattleCamp battleCamp = skillConfig.TagFriendly != 0 ? caster.Camp : caster.GetEnemyCamp();
@@ -286,7 +291,7 @@
                 var fromSkill = skillBase.fromSkill;
                 if (fromSkill != null)
                 {
-                    returnIndex = GetMainTargetPositionNum(fromSkill, fromSkill.caster, fromSkill.tagUseSkillAttack.HurtList.ToList(), fromSkill.skillConfig);
+                    returnIndex = GetMainTargetPositionNum(fromSkill, fromSkill.caster, fromSkill.tagUseSkillAttack.HurtList, fromSkill.skillConfig);
                 }
                 else
                 {
diff --git a/Main/System/Battle/Buff/BattleObjectBuffMgr.cs b/Main/System/Battle/Buff/BattleObjectBuffMgr.cs
index 20013c0..63faa2d 100644
--- a/Main/System/Battle/Buff/BattleObjectBuffMgr.cs
+++ b/Main/System/Battle/Buff/BattleObjectBuffMgr.cs
@@ -27,6 +27,12 @@
         { BattleConst.PassiveSkillLimitGroup, false },
     };
 
+    // 缂撳瓨Run()娓呯悊鐢ㄧ殑鍒楄〃锛岄伩鍏嶆瘡甯у垎閰�
+    private List<int> _removeEffectCache = new List<int>();
+
+    // 缂撳瓨OnBuffChanged鍥炶皟鐢ㄧ殑鍒楄〃锛岄伩鍏嶆瘡娆″垎閰�
+    private List<HB428_tagSCBuffRefresh> _buffValueCache = new List<HB428_tagSCBuffRefresh>();
+
     public void Init(BattleObject _battleObject)
     {
         battleObject = _battleObject;
@@ -48,7 +54,7 @@
 
     public void Run()
     {
-        List<int> removeEffectList = new List<int>();
+        _removeEffectCache.Clear();
         //  璺熼殢BattleObject
         foreach (var kv in buffEffectDict)
         {
@@ -58,7 +64,7 @@
                 if (effectPlayer.isBindBone)
                 {
                     effectPlayer.FollowBoneXY();
-                    return;
+                    continue;
                 }
                 int[] effectPos = effectPlayer.effectConfig.effectPos;
                 effectPlayer.transform.position = battleObject.GetPosition();
@@ -69,11 +75,11 @@
             }
             else
             {
-                removeEffectList.Add(kv.Key);
+                _removeEffectCache.Add(kv.Key);
             }
         }
         
-        foreach (var effectId in removeEffectList)
+        foreach (var effectId in _removeEffectCache)
         {
             buffEffectDict.Remove(effectId);
         }
@@ -234,8 +240,15 @@
             return;
         }
 
-        var buffList = vNetDataList.Where(buff => buff != null && buff.IsAdd != 0).ToList();
-        var refreshList = vNetDataList.Where(buff => buff != null && buff.IsAdd == 0).ToList();
+        var buffList = new List<HB428_tagSCBuffRefresh>();
+        var refreshList = new List<HB428_tagSCBuffRefresh>();
+        for (int i = 0; i < vNetDataList.Count; i++)
+        {
+            var buff = vNetDataList[i];
+            if (buff == null) continue;
+            if (buff.IsAdd != 0) buffList.Add(buff);
+            else refreshList.Add(buff);
+        }
 
         // 澶勭悊闇�瑕佹挱鏀惧姩鐢荤殑buff (IsAdd != 0)
         if (buffList.Count > 0)
@@ -345,7 +358,10 @@
 
         UpdateControlState();
 
-        battleObject.RefreshBuff(buffDataDict.Values.ToList());
+        _buffValueCache.Clear();
+        foreach (var kv in buffDataDict)
+            _buffValueCache.Add(kv.Value);
+        battleObject.RefreshBuff(_buffValueCache);
         onBuffChanged?.Invoke();
 
         // bool isUnderControl = false;
@@ -415,11 +431,14 @@
 
     public List<HB428_tagSCBuffRefresh> GetBuffIconList()
     {
-        List<HB428_tagSCBuffRefresh> buffList = buffDataDict.Values.Where(buff =>
+        List<HB428_tagSCBuffRefresh> buffList = new List<HB428_tagSCBuffRefresh>();
+        foreach (var kv in buffDataDict)
         {
+            var buff = kv.Value;
             SkillConfig skillConfig = SkillConfig.Get((int)buff.SkillID);
-            return skillConfig != null;
-        }).ToList();
+            if (skillConfig != null)
+                buffList.Add(buff);
+        }
         return buffList;
     }
 
diff --git a/Main/System/Battle/Motion/MotionBase.cs b/Main/System/Battle/Motion/MotionBase.cs
index ce84ec0..6621627 100644
--- a/Main/System/Battle/Motion/MotionBase.cs
+++ b/Main/System/Battle/Motion/MotionBase.cs
@@ -757,4 +757,44 @@
     {
         return !playingSkillWithAnim;
     }
+
+    /// <summary>
+    /// 寮哄埗閲嶇疆鎶�鑳藉姩鐢荤姸鎬佹爣璁�
+    /// 鐢ㄤ簬鎶�鑳紽orceFinish鍚庣‘淇濅笉浼氶樆濉炲悗缁妧鑳�
+    /// </summary>
+    public void ForceResetSkillState()
+    {
+        // 娓呯悊鎵�鏈夋妧鑳借建閬擄紙淇濈暀姝讳骸杞ㄩ亾9锛�
+        List<int> tracksToRemove = new List<int>();
+        foreach (var kv in activeSkillTracks)
+        {
+            if (kv.Key != DeathTrackIndex)
+            {
+                tracksToRemove.Add(kv.Key);
+                if (animState != null)
+                {
+                    animState.ClearTrack(kv.Key);
+                }
+            }
+        }
+        foreach (int track in tracksToRemove)
+        {
+            activeSkillTracks.Remove(track);
+        }
+
+        // 鍥炴敹鎵�鏈夊瓙鎶�鑳借建閬�
+        foreach (var kv in subSkillTrackMap)
+        {
+            if (availableSubTracks != null)
+                availableSubTracks.Enqueue(kv.Value);
+        }
+        subSkillTrackMap.Clear();
+
+        // 閲嶇疆鏍囪
+        if (!HasActiveSkillTracks())
+        {
+            playingSkill = false;
+            playingSkillWithAnim = false;
+        }
+    }
 }
\ No newline at end of file
diff --git a/Main/System/Battle/RecordPlayer/RecordAction.cs b/Main/System/Battle/RecordPlayer/RecordAction.cs
index 85a0a2c..b6c233e 100644
--- a/Main/System/Battle/RecordPlayer/RecordAction.cs
+++ b/Main/System/Battle/RecordPlayer/RecordAction.cs
@@ -41,8 +41,14 @@
     //  鑷韩鍔ㄤ綔鏄惁瀹屾垚锛堜笉鍖呮嫭瀛愯妭鐐圭殑瀹屾垚鐘舵�侊級
     protected bool isActionCompleted = false;
 
+    //  ===== 鍗℃妫�娴嬫満鍒� =====
+    //  绱杩愯鏃堕棿锛堜粎鍦≧un()琚皟鐢ㄦ椂绱姞锛屾殏鍋滄椂涓嶈鏃讹級
+    private float accumulatedRunTime = 0f;
+    //  鏈�澶у厑璁歌繍琛屾椂闀匡紙绉掞級锛岃秴杩囧垯寮哄埗缁撴潫
+    protected virtual float MaxActionDuration => 15f;
+
     //  ===== 鍐呴儴RecordPlayer鏈哄埗 =====
-    //  鍐呴儴RecordPlayer锛氱敤浜庢挱鏀剧敱姝ecordAction鍐呴儴浜х敓鐨凴ecordAction
+    //  鍐呴儴RecordPlayer锛氾拷锟斤拷浜庢挱鏀剧敱姝ecordAction鍐呴儴浜х敓鐨凴ecordAction
     //  褰揜ecordAction鍐呴儴浜х敓鏂扮殑RecordAction鏃讹紙濡侾ackageRegedit.Distribute瑙﹀彂鐨勶級锛�
     //  杩欎簺RecordAction搴旇鐢卞綋鍓峈ecordAction鐨刬nnerRecordPlayer绠$悊锛岃�屼笉鏄疊attleField鐨勪富RecordPlayer
     //  杩欐牱鍙互淇濊瘉锛�
@@ -177,8 +183,26 @@
         return true;
     }
 
+    //  妫�娴嬪綋鍓岮ction鏄惁鍗℃锛堣繍琛屾椂闂磋秴杩囧厑璁镐笂闄愶級
+    //  娉ㄦ剰锛氫娇鐢ㄧ疮璁℃椂闂磋�岄潪Time.time锛岃繖鏍锋殏鍋滄湡闂翠笉浼氳鏃�
+    public bool IsStuck()
+    {
+        if (accumulatedRunTime <= 0f) return false;
+        if (isFinish || isActionCompleted) return false;
+        return accumulatedRunTime > MaxActionDuration;
+    }
+
+    //  鑾峰彇宸茶繍琛屾椂闀�
+    public float GetRunDuration()
+    {
+        return accumulatedRunTime;
+    }
+
     public virtual void Run()
     {
+        //  绱姞杩愯鏃堕棿锛堟殏鍋滄椂BattleField.Run涓嶈皟鐢≧ecordPlayer锛屾墍浠ユ澶勪笉浼氱疮鍔狅級
+        accumulatedRunTime += Time.deltaTime;
+
 #if UNITY_EDITOR
         // 棣栨杩愯鏃舵墦鍗拌皟璇曚俊鎭�
         if (!hasLoggedFirstRun)
diff --git a/Main/System/Battle/RecordPlayer/RecordPlayer.cs b/Main/System/Battle/RecordPlayer/RecordPlayer.cs
index 35a6f77..09f140e 100644
--- a/Main/System/Battle/RecordPlayer/RecordPlayer.cs
+++ b/Main/System/Battle/RecordPlayer/RecordPlayer.cs
@@ -259,6 +259,15 @@
                 
                 if (!action.IsFinished())
                 {
+                    //  鍗℃妫�娴嬶細濡傛灉immediately action杩愯鏃堕棿杩囬暱锛屽己鍒剁粨鏉�
+                    if (action.IsStuck())
+                    {
+                        BattleDebug.LogError($"RecordPlayer: ImmediatelyAction {action.GetType().Name} (ID:{action.actionID}) 杩愯 {action.GetRunDuration():F1}s 瓒呮椂锛屽己鍒剁粨鏉燂紒");
+                        action.ForceFinish();
+                        removeIndexList.Add(i);
+                        continue;
+                    }
+
                     if (action.waitingAnimeAction != null)
                     {
                         if (!action.waitingAnimeAction.IsActionCompleted())
@@ -350,6 +359,15 @@
 
         if (currentRecordAction != null && !currentRecordAction.IsFinished())
         {
+            //  鍗℃妫�娴嬶細濡傛灉currentRecordAction杩愯鏃堕棿杩囬暱锛屽己鍒剁粨鏉�
+            if (currentRecordAction.IsStuck())
+            {
+                BattleDebug.LogError($"RecordPlayer: CurrentAction {currentRecordAction.GetType().Name} (ID:{currentRecordAction.actionID}) 杩愯 {currentRecordAction.GetRunDuration():F1}s 瓒呮椂锛屽己鍒剁粨鏉燂紒");
+                currentRecordAction.ForceFinish();
+                currentRecordAction = null;
+                return;
+            }
+
             if (currentRecordAction.waitingAnimeAction != null)
             {
                 if (!currentRecordAction.waitingAnimeAction.IsActionCompleted())
diff --git a/Main/System/Battle/Skill/SkillBase.cs b/Main/System/Battle/Skill/SkillBase.cs
index ba8647d..83da046 100644
--- a/Main/System/Battle/Skill/SkillBase.cs
+++ b/Main/System/Battle/Skill/SkillBase.cs
@@ -195,7 +195,7 @@
         
     }
 
-    // 鎶�鑳借繍琛屼富閫昏緫锛氬鐞嗘妧鑳芥晥鏋滃拰鍏朵粬鎶�鑳藉姩浣�
+    // 鎶�鑳借繍琛屼富閫昏緫锛氫粎椹卞姩鎶�鑳芥晥鏋滐紙skillEffect锛夛紝瀛愭妧鑳藉拰姝讳骸鐢盜sFinished()鎺ㄨ繘
     public virtual void Run()
     {
         if (skillEffect != null)
@@ -211,8 +211,6 @@
             }
             return;
         }
-
-
     }
 
     protected void ShadowIllutionCreate(bool create)
@@ -370,7 +368,7 @@
             return;
         }
 
-        int mainTargetPosNum = BattleUtility.GetMainTargetPositionNum(this, caster, tagUseSkillAttack.HurtList.ToList(), skillConfig);
+        int mainTargetPosNum = BattleUtility.GetMainTargetPositionNum(this, caster, tagUseSkillAttack.HurtList, skillConfig);
         BattleCamp battleCamp = skillConfig.TagFriendly != 0 ? caster.Camp : caster.GetEnemyCamp();
         RectTransform targetTrans = battleField.GetTeamNode(battleCamp, mainTargetPosNum);
 
@@ -482,9 +480,9 @@
     protected virtual void OnAllAttackMoveFinished()
     {
         moveFinished = true;
-        List<BattleObject> allList = battleField.battleObjMgr.allBattleObjDict.Values.ToList<BattleObject>();
-        foreach (BattleObject bo in allList)
+        foreach (var kv in battleField.battleObjMgr.allBattleObjDict)
         {
+            BattleObject bo = kv.Value;
             bo.layerMgr.SetFront();
             bo.GetHeroInfoBar()?.SetActive(true);
         }
@@ -672,7 +670,7 @@
         // 纭繚鏂芥硶鑰呬篃琚珮浜紙鍘熼�昏緫锛�
         var highlightList = new List<BattleObject>(targetSet) { caster };
 
-        var allList = battleField.battleObjMgr.allBattleObjDict.Values.ToList();
+        var allList = battleField.battleObjMgr.allBattleObjDict.Values;
 
         // 鏋勯�犻泦鍚堜究浜庡垽鏂�
         var targetSetLookup = new HashSet<BattleObject>(targetSet);
@@ -1022,7 +1020,12 @@
         // 鑾峰彇骞跺垎閰嶆帀钀界墿鍝佸拰缁忛獙
         var dropPack = PackManager.Instance.GetSinglePack(PackType.DropItem);
         var itemDict = dropPack.GetAllItems();
-        List<ItemModel> itemList = new List<ItemModel>(itemDict.Values.Where(item => item != null && item.isAuction));
+        List<ItemModel> itemList = new List<ItemModel>();
+        foreach (var item in itemDict.Values)
+        {
+            if (item != null && item.isAuction)
+                itemList.Add(item);
+        }
 
         var dropAssign = AssignDrops(itemList, deadPackList.Count);
         var expAssign = AssignExp(expPackList, deadPackList.Count);
@@ -1041,7 +1044,9 @@
                 continue;
             }
             
-            List<int> itemIndexList = dropAssign[i].Select(item => item.gridIndex).ToList();
+            List<int> itemIndexList = new List<int>(dropAssign[i].Count);
+            for (int j = 0; j < dropAssign[i].Count; j++)
+                itemIndexList.Add(dropAssign[i][j].gridIndex);
             
             BattleDrops battleDrops = new BattleDrops()
             {
@@ -1238,7 +1243,8 @@
         return false;
     }
 
-    // 妫�鏌ユ妧鑳芥槸鍚﹀畬鎴愶細缁煎悎妫�鏌ユ墍鏈夊畬鎴愭潯浠�
+    // 妫�鏌ユ妧鑳芥槸鍚﹀畬鎴愶細鍚屾椂鎺ㄨ繘鐘舵�侊紙娓呯悊瀹屾垚鐨勫瓙鎶�鑳姐�佸鐞嗗墿浣欏寘銆佽Е鍙戞浜″垽瀹氾級
+    // 娉ㄦ剰锛氭鏂规硶鏈夊壇浣滅敤锛岃繖鏄璁′娇鐒垛�斺�旂敱SkillRecordAction.Run()姣忓抚璋冪敤鏉ラ┍鍔ㄧ姸鎬佹帹杩�
     public virtual bool IsFinished()
     {
         if (!isPlay) return false;
@@ -1254,7 +1260,7 @@
             tempRetValue = false;
         }
 
-        // 妫�鏌ュ叾浠栨妧鑳藉姩浣滄槸鍚﹀畬鎴�
+        // 妫�鏌ヨ窡杩涚殑鎶�鑳藉姩浣滄槸鍚﹀畬鎴愶紙杩藉嚮/杩炲嚮/鍙嶅嚮绛夛級
         if (currentWaitingSkill.Count > 0)
         {
             if (currentWaitingSkill.Any(s => s.IsFinished()))
@@ -1273,7 +1279,6 @@
             return false;
         }
 
-
         // 妫�鏌ユ渶缁堝畬鎴愮姸鎬�
         if (isFinished && moveFinished)
         {
@@ -1283,7 +1288,7 @@
                 return false;
             }
 
-            //  濡傛灉鑷繁鍐呴儴鐨剅ecora action鐨� inner record player杩樻湁娌℃墽琛屽畬鐨勫寘 涔熸槸杩斿洖false
+            //  濡傛灉鑷繁鍐呴儴鐨� innerRecordPlayer 杩樻湁娌℃墽琛屽畬鐨勫寘 涔熸槸杩斿洖false
             if (ownRecordAction != null && ownRecordAction.GetInnerRecordPlayer().IsPlaying())
             {
                 return false;
@@ -1294,7 +1299,6 @@
             {
                 battleField.RemoveCastingSkill(caster.ObjID, this);
                 
-                //  浼犻�抪arentRecordAction锛岃姝讳骸鎶�鑳界瓑寰呭綋鍓嶆妧鑳藉畬鎴�
                 DeathRecordAction recordAction = battleField.OnObjsDead(new List<BattleDeadPack>(tempDeadPackList.Values), null, ownRecordAction);
                 if (null != recordAction)
                 {
@@ -1362,6 +1366,12 @@
 
             // 鍙栨秷骞诲奖鏁堟灉
             caster.ShowIllusionShadow(false);
+
+            // 閲嶇疆MotionBase鐨勬妧鑳藉姩鐢荤姸鎬侊紝闃叉playingSkillWithAnim鍗′綇鍚庣画鎶�鑳�
+            if (caster is HeroBattleObject heroCaster)
+            {
+                heroCaster.ForceResetMotionSkillState();
+            }
         }
 
         // 5. 鎭㈠ UI 鐘舵��
@@ -1490,14 +1500,22 @@
                 skillRecordAction.fromSkill = this;
                 currentWaitingSkill.Add(skillRecordAction);
 
-                //  闇�瑕佺粰鐪熸parent鎾殑
+                //  鏍规嵁鍚庣画鎶�鑳界殑灞炴�у喅瀹氭槸鍚﹂渶瑕佺瓑寰呭綋鍓嶆妧鑳藉綊浣�
+                RecordAction waitAction = GetFollowUpWaitAction(skillRecordAction);
+
                 if (skillRecordAction.useParentRecordPlayer && skillRecordAction.parentSkillAction != null)
                 {
-                    skillRecordAction.parentSkillAction.GetInnerRecordPlayer().PlayRecord(skillRecordAction);
+                    if (waitAction != null)
+                        skillRecordAction.parentSkillAction.GetInnerRecordPlayer().PlayRecord(skillRecordAction, waitAction);
+                    else
+                        skillRecordAction.parentSkillAction.GetInnerRecordPlayer().PlayRecord(skillRecordAction);
                 }
                 else
                 {
-                    ownRecordAction.GetInnerRecordPlayer().PlayRecord(skillRecordAction);
+                    if (waitAction != null)
+                        ownRecordAction.GetInnerRecordPlayer().PlayRecord(skillRecordAction, waitAction);
+                    else
+                        ownRecordAction.GetInnerRecordPlayer().PlayRecord(skillRecordAction);
                 }
 
                 return false;
@@ -1539,6 +1557,40 @@
         return true;
     }
 
+    /// <summary>
+    /// 鍒ゅ畾鍚庣画鎶�鑳斤紙杩藉嚮/杩炲嚮/鍙嶅嚮绛夛級鏄惁闇�瑕佺瓑寰呭綋鍓嶆妧鑳藉畬鎴愬姩浣滃悗鍐嶉噴鏀�
+    /// 缁煎悎鍒ゅ埆锛氭妧鑳芥槸鍚︽湁鍔ㄧ敾銆侀噴鏀炬ā寮忔槸鍚﹂渶瑕佷綅绉汇�佹槸鍚﹀悓涓�鏂芥硶鑰�
+    /// 浠呭湪纭瀹夊叏鏃惰繑鍥瀢aitAction锛岄伩鍏嶄笉蹇呰鐨勭瓑寰呭拰娼滃湪鐨勮法瑙掕壊鐩镐簰绛夊緟
+    /// </summary>
+    private RecordAction GetFollowUpWaitAction(SkillRecordAction followUp)
+    {
+        // 褰撳墠鎶�鑳藉凡缁忓綊浣嶏紝鏃犻渶绛夊緟
+        if (moveFinished) return null;
+
+        if (followUp.skillBase == null) return null;
+
+        // 鍚庣画鎶�鑳芥病鏈夊姩鐢诲姩浣滐紙鏃燬killMotionName锛夛紝涓嶆秹鍙婁綅绉伙紝鏃犻渶绛夊緟褰掍綅
+        // 杩欑被鎶�鑳藉湪CanCastSkill涓篃涓嶅彈playingSkillWithAnim闄愬埗
+        var followUpSkin = followUp.skillBase.skillSkinConfig;
+        if (followUpSkin == null || string.IsNullOrEmpty(followUpSkin.SkillMotionName))
+            return null;
+
+        // 鍚庣画鎶�鑳藉師鍦伴噴鏀撅紙None/Self锛夛紝铏芥湁鍔ㄧ敾浣嗕笉闇�瑕佷綅绉诲埌鐩爣锛屾棤闇�绛夊緟
+        if (followUpSkin.castMode == SkillCastMode.None || followUpSkin.castMode == SkillCastMode.Self)
+            return null;
+
+        // 鍚屼竴鏂芥硶鑰呯殑鍚庣画鎶�鑳斤紙杩炲嚮绛夛級锛氬繀椤荤瓑寰呭綊浣�
+        // 鍘熷洜锛氬悓涓�瑙掕壊鐨凜astToEnemy/CastToTarget浼歁oveToTarget锛�
+        // 濡傛灉涓婁竴涓妧鑳界殑DOTween褰掍綅杩樻病瀹屾垚灏卞彂璧锋柊浣嶇Щ锛屼袱涓狣OTween浼氬啿绐�
+        if (followUp.skillBase.caster == caster)
+            return ownRecordAction;
+
+        // 涓嶅悓鏂芥硶鑰呯殑鍚庣画鎶�鑳斤紙杩藉嚮/鍙嶅嚮绛夛級锛氫笉娣诲姞绛夊緟
+        // 鍘熷洜锛氫笉鍚岃鑹蹭粠鍚勮嚜浣嶇疆鍑哄彂锛屼笉瀛樺湪DOTween鍐茬獊
+        // 娉ㄦ剰锛氭澶勪笉娣诲姞璺ㄨ鑹茬瓑寰呭叧绯伙紝閬垮厤鎶�鑳介摼A绛塀銆丅绛堿鐨勭浉浜掔瓑寰呴闄�
+        return null;
+    }
+
     // 娣诲姞娓呯悊鏂规硶锛氶槻姝㈠唴瀛樻硠婕�
     public virtual void Cleanup()
     {
diff --git a/Main/System/Battle/SkillEffect/BulletSkillEffect.cs b/Main/System/Battle/SkillEffect/BulletSkillEffect.cs
index 1022169..eda6793 100644
--- a/Main/System/Battle/SkillEffect/BulletSkillEffect.cs
+++ b/Main/System/Battle/SkillEffect/BulletSkillEffect.cs
@@ -176,7 +176,7 @@
 
         RectTransform effectTrans = effectPlayer.transform as RectTransform;
 
-        var bulletCurve = BulletCurveFactory.CreateBulletCurve(caster, skillConfig, skillSkinConfig, effectPlayer, targetTransform, tagUseSkillAttack.HurtList.ToList(), bulletIndex, (index, hitList) =>
+        var bulletCurve = BulletCurveFactory.CreateBulletCurve(caster, skillConfig, skillSkinConfig, effectPlayer, targetTransform, HurtListAsList, bulletIndex, (index, hitList) =>
         {
             if (isFinish)
                 return;
@@ -384,7 +384,7 @@
 
         int tempBulletIndex = bulletIndex;
 
-        var bulletCurve = BulletCurveFactory.CreateBulletCurve(caster, skillConfig, skillSkinConfig, effectPlayer, target.GetRectTransform(), tagUseSkillAttack.HurtList.ToList(), bulletIndex, (index, hitList) =>
+        var bulletCurve = BulletCurveFactory.CreateBulletCurve(caster, skillConfig, skillSkinConfig, effectPlayer, target.GetRectTransform(), HurtListAsList, bulletIndex, (index, hitList) =>
         {
             if (skillSkinConfig.BulletPath == 4)
             {
diff --git a/Main/System/Battle/SkillEffect/DotSkillEffect.cs b/Main/System/Battle/SkillEffect/DotSkillEffect.cs
index d1cdea6..edab617 100644
--- a/Main/System/Battle/SkillEffect/DotSkillEffect.cs
+++ b/Main/System/Battle/SkillEffect/DotSkillEffect.cs
@@ -34,7 +34,7 @@
             target.battleField.battleEffectMgr.PlayEffect(caster, skillSkinConfig.TriggerEffect, target.GetRectTransform(), caster.Camp, target.GetModelScale());
         }
 
-        onHit?.Invoke(0, tagUseSkillAttack.HurtList.ToList());
+        onHit?.Invoke(0, HurtListAsList);
         isFinish = true;
     }
 
diff --git a/Main/System/Battle/SkillEffect/NoEffect.cs b/Main/System/Battle/SkillEffect/NoEffect.cs
index f5f4a2f..a2cc415 100644
--- a/Main/System/Battle/SkillEffect/NoEffect.cs
+++ b/Main/System/Battle/SkillEffect/NoEffect.cs
@@ -22,7 +22,7 @@
 
     public override void OnMiddleFrameEnd(int times, int hitIndex)
     {
-        int mainTargetIndex = BattleUtility.GetMainTargetPositionNum(skillBase, caster, tagUseSkillAttack.HurtList.ToList(), skillConfig);
+        int mainTargetIndex = BattleUtility.GetMainTargetPositionNum(skillBase, caster, tagUseSkillAttack.HurtList, skillConfig);
 
         BattleCamp battleCamp = skillConfig.TagFriendly == 1 ? caster.Camp : caster.GetEnemyCamp();
 
@@ -58,7 +58,7 @@
             }
         }
 
-        onHit?.Invoke(hitIndex, tagUseSkillAttack.HurtList.ToList());
+        onHit?.Invoke(hitIndex, HurtListAsList);
     }
 
     /// <summary>
diff --git a/Main/System/Battle/SkillEffect/NormalSkillEffect.cs b/Main/System/Battle/SkillEffect/NormalSkillEffect.cs
index 31b26ad..1a2f1e3 100644
--- a/Main/System/Battle/SkillEffect/NormalSkillEffect.cs
+++ b/Main/System/Battle/SkillEffect/NormalSkillEffect.cs
@@ -21,7 +21,7 @@
     public override void OnMiddleFrameEnd(int times, int hitIndex)
     {
 
-        int mainTargetIndex = BattleUtility.GetMainTargetPositionNum(skillBase, caster, tagUseSkillAttack.HurtList.ToList(), skillConfig);
+        int mainTargetIndex = BattleUtility.GetMainTargetPositionNum(skillBase, caster, tagUseSkillAttack.HurtList, skillConfig);
 
         BattleCamp battleCamp = skillConfig.TagFriendly == 1 ? caster.Camp : caster.GetEnemyCamp();
 
@@ -57,7 +57,7 @@
             }
         }
 
-        onHit?.Invoke(hitIndex, tagUseSkillAttack.HurtList.ToList());
+        onHit?.Invoke(hitIndex, HurtListAsList);
     }
 
     /// <summary>
diff --git a/Main/System/Battle/SkillEffect/SkillEffect.cs b/Main/System/Battle/SkillEffect/SkillEffect.cs
index 3fecf16..01a4d36 100644
--- a/Main/System/Battle/SkillEffect/SkillEffect.cs
+++ b/Main/System/Battle/SkillEffect/SkillEffect.cs
@@ -16,6 +16,18 @@
 
     protected Action<int, List<HB427_tagSCUseSkill.tagSCUseSkillHurt>> onHit;
 
+    // 缂撳瓨HurtList鐨凩ist鍓湰锛岄伩鍏嶆瘡娆nHit鍥炶皟鏃堕噸澶峊oList鍒嗛厤
+    private List<HB427_tagSCUseSkill.tagSCUseSkillHurt> _hurtListCache;
+    protected List<HB427_tagSCUseSkill.tagSCUseSkillHurt> HurtListAsList
+    {
+        get
+        {
+            if (_hurtListCache == null)
+                _hurtListCache = new List<HB427_tagSCUseSkill.tagSCUseSkillHurt>(tagUseSkillAttack.HurtList);
+            return _hurtListCache;
+        }
+    }
+
     public SkillEffect(SkillBase _skillBase, SkillConfig _skillConfig, SkillSkinConfig _skillSkinConfig, BattleObject _caster, HB427_tagSCUseSkill _tagUseSkillAttack)
     {
         skillBase = _skillBase;
diff --git a/Main/System/Battle/Sound/BattleSoundManager.cs b/Main/System/Battle/Sound/BattleSoundManager.cs
index f0008fc..61edfd6 100644
--- a/Main/System/Battle/Sound/BattleSoundManager.cs
+++ b/Main/System/Battle/Sound/BattleSoundManager.cs
@@ -22,11 +22,17 @@
     // 璁板綍姣忎釜闊虫晥ID褰撳墠鎾斁鐨凙udioSource
     private Dictionary<int, List<AudioSource>> audioIdToSources = new Dictionary<int, List<AudioSource>>();
     
+    // 娓呯悊鐢ㄧ紦瀛樺垪琛紝閬垮厤姣忓抚鍒嗛厤
+    private List<int> _keysToRemoveCache = new List<int>();
+    
     // 闊抽鍓緫缂撳瓨
     private Dictionary<int, AudioClip> audioClipCache = new Dictionary<int, AudioClip>();
     
     // 褰撳墠鎾斁閫熷害
     private float currentSpeedRatio = 1f;
+    
+    // 璺熻釜AudioSource鎬绘暟锛岄伩鍏岹etComponents鍒嗛厤
+    private int audioSourceCount = 0;
     
     // 鏄惁鏈夌劍鐐�
     private bool hasFocus = true;
@@ -111,6 +117,7 @@
             source.spatialBlend = 0f; // 2D闊虫晥
             audioSourcePool.Enqueue(source);
         }
+        audioSourceCount = INITIAL_AUDIO_POOL;
         Debug.Log($"<color=cyan>BattleSoundManager [{battleField.guid}]: 鍒濆鍖栦簡 {INITIAL_AUDIO_POOL} 涓� AudioSource</color>");
     }
     
@@ -145,21 +152,27 @@
         // 妫�鏌ユ槸鍚︽湁鐒︾偣锛屾棤鐒︾偣鏃朵笉鎾斁
         if (!hasFocus)
         {
+#if UNITY_EDITOR
             Debug.Log($"<color=yellow>BattleSoundManager [{battleField.guid}]: 鏃犵劍鐐癸紝鎷掔粷鎾斁闊虫晥 {audioId}</color>");
+#endif
             return;
         }
         
         // 妫�鏌ヨ闊虫晥鏄惁宸茶揪鍒版挱鏀句笂闄�
         if (!CanPlayAudio(audioId))
         {
+#if UNITY_EDITOR
             Debug.Log($"<color=yellow>BattleSoundManager [{battleField.guid}]: 闊虫晥 {audioId} 杈惧埌鎾斁涓婇檺锛屾嫆缁濇挱鏀�</color>");
+#endif
             return;
         }
         
         var audioClip = await GetAudioClip(audioId);
         if (audioClip == null)
         {
+#if UNITY_EDITOR
             Debug.Log($"<color=red>BattleSoundManager [{battleField.guid}]: 鏃犳硶鍔犺浇闊虫晥 {audioId}</color>");
+#endif
             return;
         }
         
@@ -167,7 +180,9 @@
         AudioSource source = GetAvailableAudioSource();
         if (source == null)
         {
+#if UNITY_EDITOR
             Debug.Log($"<color=red>BattleSoundManager [{battleField.guid}]: 鏃犳硶鑾峰彇AudioSource锛屾睜鏁伴噺={audioSourcePool.Count}锛屾椿璺冩暟閲�={activeAudioSources.Count}</color>");
+#endif
             return;
         }
         
@@ -190,7 +205,9 @@
         source.clip = audioClip;
         source.Play();
         
+#if UNITY_EDITOR
         Debug.Log($"<color=green>BattleSoundManager [{battleField.guid}]: 鎾斁闊虫晥 {audioId} - {audioClip.name}</color>");
+#endif
         
         // 鏍囪涓烘椿璺�
         if (!activeAudioSources.Contains(source))
@@ -281,16 +298,18 @@
             {
                 return source;
             }
+#if UNITY_EDITOR
             Debug.Log($"<color=orange>BattleSoundManager [{battleField.guid}]: 姹犱腑鐨凙udioSource宸茶閿�姣侊紝璺宠繃</color>");
+#endif
         }
         
         // 璁$畻褰撳墠鎬荤殑 AudioSource 鏁伴噺
-        int totalAudioSources = audioSourceObject.GetComponents<AudioSource>().Length;
-        
-        if (totalAudioSources >= MAX_AUDIO_SOURCES)
+        if (audioSourceCount >= MAX_AUDIO_SOURCES)
         {
             // 杈惧埌涓婇檺锛屼笉鍐嶅垱寤猴紝涓㈠純杩欐鎾斁璇锋眰
+#if UNITY_EDITOR
             Debug.Log($"BattleSoundManager: AudioSource 鏁伴噺宸茶揪涓婇檺 {MAX_AUDIO_SOURCES}锛屾棤娉曟挱鏀炬柊闊虫晥");
+#endif
             return null;
         }
         
@@ -299,8 +318,11 @@
         newSource.playOnAwake = false;
         newSource.loop = false;
         newSource.spatialBlend = 0f; // 2D闊虫晥
+        audioSourceCount++;
         
-        Debug.Log($"<color=cyan>BattleSoundManager [{battleField.guid}]: 鍔ㄦ�佸垱寤烘柊AudioSource锛屽綋鍓嶆�绘暟={totalAudioSources + 1}</color>");
+#if UNITY_EDITOR
+        Debug.Log($"<color=cyan>BattleSoundManager [{battleField.guid}]: 鍔ㄦ�佸垱寤烘柊AudioSource锛屽綋鍓嶆�绘暟={audioSourceCount}</color>");
+#endif
         
         return newSource;
     }
@@ -328,18 +350,18 @@
         }
         
         // 娓呯悊 audioIdToSources 涓凡鍋滄鎾斁鐨� AudioSource
-        var keysToRemove = new List<int>();
+        _keysToRemoveCache.Clear();
         foreach (var kvp in audioIdToSources)
         {
             kvp.Value.RemoveAll(s => s == null || !s.isPlaying);
             if (kvp.Value.Count == 0)
             {
-                keysToRemove.Add(kvp.Key);
+                _keysToRemoveCache.Add(kvp.Key);
             }
         }
         
         // 绉婚櫎绌虹殑闊虫晥ID璁板綍
-        foreach (var key in keysToRemove)
+        foreach (var key in _keysToRemoveCache)
         {
             audioIdToSources.Remove(key);
         }
@@ -420,11 +442,15 @@
         int activeCount = activeAudioSources.Count;
         int totalStopped = 0;
         
+        AudioSource[] allSources = null;
+        
         // 涓嶄緷璧栧垪琛紝鐩存帴鍋滄 GameObject 涓婄殑鎵�鏈� AudioSource
         if (audioSourceObject != null)
         {
-            var allSources = audioSourceObject.GetComponents<AudioSource>();
+            allSources = audioSourceObject.GetComponents<AudioSource>();
+#if UNITY_EDITOR
             Debug.Log($"<color=red>BattleSoundManager [{battleField.guid}]: StopAllSounds - GameObject涓婂叡鏈� {allSources.Length} 涓狝udioSource</color>");
+#endif
             
             foreach (var source in allSources)
             {
@@ -432,7 +458,9 @@
                 {
                     if (source.isPlaying)
                     {
+#if UNITY_EDITOR
                         Debug.Log($"<color=red>  鍋滄姝e湪鎾斁鐨�: {source.clip?.name}</color>");
+#endif
                         totalStopped++;
                     }
                     source.Stop();
@@ -441,17 +469,18 @@
             }
         }
         
+#if UNITY_EDITOR
         Debug.Log($"<color=red>BattleSoundManager [{battleField.guid}]: StopAllSounds - 娲昏穬鍒楄〃={activeCount}, 瀹為檯鍋滄={totalStopped}</color>");
+#endif
         
         // 娓呯┖鎵�鏈夊垪琛�
         activeAudioSources.Clear();
         audioIdToSources.Clear();
         
-        // 閲嶅缓瀵硅薄姹�
+        // 閲嶅缓瀵硅薄姹狅紙澶嶇敤涓婇潰宸茶幏鍙栫殑鏁扮粍锛�
         audioSourcePool.Clear();
-        if (audioSourceObject != null)
+        if (allSources != null)
         {
-            var allSources = audioSourceObject.GetComponents<AudioSource>();
             foreach (var source in allSources)
             {
                 if (source != null)
@@ -461,7 +490,9 @@
             }
         }
         
+#if UNITY_EDITOR
         Debug.Log($"<color=red>BattleSoundManager [{battleField.guid}]: StopAllSounds 瀹屾垚</color>");
+#endif
     }
     
     /// <summary>
diff --git a/Main/System/Equip/EquipTipWin.cs b/Main/System/Equip/EquipTipWin.cs
index d198f32..5f2c68c 100644
--- a/Main/System/Equip/EquipTipWin.cs
+++ b/Main/System/Equip/EquipTipWin.cs
@@ -154,14 +154,14 @@
         bgFlower.color = UIHelper.GetUIColor(itemConfig.ItemColor);
 
 
-        var baseAttrs = appointItemConfig.BaseAttrID.ToList();
-        var baseValues = appointItemConfig.BaseAttrValue.ToList();
-        var fightAttrs = appointItemConfig.LegendAttrID.ToList();
-        var fightValues = appointItemConfig.LegendAttrValue.ToList();
+        var baseAttrs = appointItemConfig.BaseAttrID;
+        var baseValues = appointItemConfig.BaseAttrValue;
+        var fightAttrs = appointItemConfig.LegendAttrID;
+        var fightValues = appointItemConfig.LegendAttrValue;
 
         for (var i = 0; i < baseAttrNames.Count; i++)
         {
-            if (i >= baseAttrs.Count)
+            if (i >= baseAttrs.Length)
             {
                 baseAttrNames[i].text = "";
                 baseAttrValues[i].text = "";
@@ -182,7 +182,7 @@
             fightAttrGameObj.SetActive(true);
             for (var i = 0; i < fightAttrNames.Count; i++)
             {
-                if (i >= fightAttrs.Count)
+                if (i >= fightAttrs.Length)
                 {
                     fightAttrNames[i].SetActive(false);
                 }
diff --git a/Main/System/Hero/UIHeroController.cs b/Main/System/Hero/UIHeroController.cs
index fbf85d8..bd23c5b 100644
--- a/Main/System/Hero/UIHeroController.cs
+++ b/Main/System/Hero/UIHeroController.cs
@@ -25,6 +25,7 @@
 	private static readonly object loadLock = new object();
 	private static int lastInitFrame = -1; // 涓婁竴娆℃墽琛孖nitialize鐨勫抚鍙凤紝鐢ㄤ簬纭繚姣忓抚鏈�澶�1娆�
 	private static GameObjectPoolManager.GameObjectPool cachedUIHeroPool; // 缂撳瓨UIHero棰勫埗浣撴睜
+	private RectTransform _instanceRect; // 缂撳瓨RectTransform閬垮厤閲嶅GetComponent
 
 	public Action onComplete;
 	public async UniTask Create(int _skinID, float scale = 0.8f, Action _onComplete = null, string motionName = "idle", bool isLh = false)
@@ -213,8 +214,9 @@
 		{
 			instanceGO = pool.Request();
 			instanceGO.transform.SetParent(transform);
+			_instanceRect = instanceGO.GetComponent<RectTransform>();
 			//transform 鐨凱ivot Y鏄�0锛岃instanceGO 灞呬腑
-			instanceGO.transform.localPosition = new Vector3(0, instanceGO.GetComponent<RectTransform>().sizeDelta.y * 0.5f);
+			instanceGO.transform.localPosition = new Vector3(0, _instanceRect.sizeDelta.y * 0.5f);
 
 			//instanceGO.transform.localPosition = Vector3.zero;
 			instanceGO.transform.localScale = Vector3.one;
@@ -670,8 +672,9 @@
 		{
 			instanceGO = pool.Request();
 			instanceGO.transform.SetParent(transform);
+			_instanceRect = instanceGO.GetComponent<RectTransform>();
 			//transform 鐨凱ivot Y鏄�0锛岃instanceGO 灞呬腑
-			instanceGO.transform.localPosition = new Vector3(0, instanceGO.GetComponent<RectTransform>().sizeDelta.y * 0.5f);
+			instanceGO.transform.localPosition = new Vector3(0, _instanceRect.sizeDelta.y * 0.5f);
 			instanceGO.transform.localScale = Vector3.one;
 			instanceGO.transform.localRotation = Quaternion.identity;
 		}
diff --git a/Main/System/Main/MainWin.cs b/Main/System/Main/MainWin.cs
index b4cf6c5..41d3f94 100644
--- a/Main/System/Main/MainWin.cs
+++ b/Main/System/Main/MainWin.cs
@@ -192,7 +192,7 @@
         // 浠庣帺瀹舵暟鎹腑鑾峰彇淇℃伅骞舵洿鏂癠I
         avatarCell.InitUI(AvatarHelper.GetAvatarModel((int)PlayerDatas.Instance.baseData.PlayerID,
                                                         PlayerDatas.Instance.baseData.face,
-                                                        PlayerDatas.Instance.baseData.facePic));
+                                                        PlayerDatas.Instance.baseData.facePic)).Forget();
 
         playerNameText.text = PlayerDatas.Instance.baseData.PlayerName;
         powerText.text = UIHelper.ReplaceLargeArtNum(PlayerDatas.Instance.baseData.FightPower);
@@ -214,7 +214,7 @@
             case PlayerDataType.FacePic:
                 avatarCell.InitUI(AvatarHelper.GetAvatarModel((int)PlayerDatas.Instance.baseData.PlayerID,
                                                                 PlayerDatas.Instance.baseData.face,
-                                                                PlayerDatas.Instance.baseData.facePic));
+                                                                PlayerDatas.Instance.baseData.facePic)).Forget();
                 break;
             case PlayerDataType.default26:
                 hammerText.text = UIHelper.GetMoneyCnt(41).ToString();
diff --git a/Main/System/PhantasmPavilion/AvatarCell.cs b/Main/System/PhantasmPavilion/AvatarCell.cs
index 50577ef..6859c9e 100644
--- a/Main/System/PhantasmPavilion/AvatarCell.cs
+++ b/Main/System/PhantasmPavilion/AvatarCell.cs
@@ -223,10 +223,13 @@
         onLoaded = null;
 
     }
-    public async void InitUI(AvatarModel model)
+    public async UniTask InitUI(AvatarModel model)
     {
         if (model == null)
+        {
+            Debug.LogError("[AvatarCell] model is null");
             return;
+        }
         avatarModel = model;
         await LoadPrefab();   //瀛樺湪琚嵏杞界殑鍙兘锛岄噸鏂板姞杞�
 
diff --git a/Main/System/Sound/SoundPlayer.cs b/Main/System/Sound/SoundPlayer.cs
index 52a353d..0ff13f4 100644
--- a/Main/System/Sound/SoundPlayer.cs
+++ b/Main/System/Sound/SoundPlayer.cs
@@ -455,7 +455,7 @@
 
     IEnumerator Co_DelayPlayBackGrondMusic(float _delay, int _audioId)
     {
-        yield return new WaitForSeconds(_delay);
+        yield return WaitForSecondsCache.Get(_delay);
         PlayBackGroundMusic(_audioId).Forget();
     }
 }
diff --git a/Main/System/SpineUpdateManager.cs b/Main/System/SpineUpdateManager.cs
index ad594de..38186c3 100644
--- a/Main/System/SpineUpdateManager.cs
+++ b/Main/System/SpineUpdateManager.cs
@@ -67,15 +67,12 @@
                 continue;
             }
             if (!sg.isActiveAndEnabled) continue;
-            // 鎵嬪姩椹卞姩鍔ㄧ敾鏇存柊锛歎pdate(float)涓嶆鏌reeze锛岀洿鎺ユ洿鏂板姩鐢荤姸鎬�
             sg.Update(dt);
         }
     }
 
     void LateUpdate()
     {
-        // 闅斿抚鏇存柊Mesh锛氬姩鐢荤姸鎬佹瘡甯ф洿鏂颁繚璇佷簨浠跺噯纭紝浣哅esh閲嶅缓锛堝紑閿�澶э級闅斿抚鍗冲彲
-        // 鏁伴噺灏戞椂姣忓抚閮芥洿鏂帮紝鏁伴噺澶氫簬8涓椂闅斿抚鏇存柊浠ュ噺灏慍PU璐熸媴
         bool skipMesh = managedSpines.Count > 8 && (frameCount & 1) == 0;
 
         for (int i = managedSpines.Count - 1; i >= 0; i--)
@@ -89,7 +86,6 @@
             }
             if (!sg.isActiveAndEnabled) continue;
             if (skipMesh) continue;
-            // 鎵嬪姩椹卞姩Mesh鏇存柊锛氱洿鎺ヨ皟鐢║pdateMesh()锛屼笉缁忚繃LateUpdate()鐨刦reeze妫�鏌�
             sg.UpdateMesh();
         }
     }
diff --git a/Main/System/Tip/MarqueeWin.cs b/Main/System/Tip/MarqueeWin.cs
index b4fe9f2..bbd7cf6 100644
--- a/Main/System/Tip/MarqueeWin.cs
+++ b/Main/System/Tip/MarqueeWin.cs
@@ -74,7 +74,7 @@
 
     IEnumerator Co_StartTween()
     {
-        yield return new WaitForSeconds(5f);
+        yield return WaitForSecondsCache.Wait5;
         m_ContainerMarquee.SetActive(true);
         BeginMarquee();
     }
diff --git a/Main/System/Tip/ScrollTip.cs b/Main/System/Tip/ScrollTip.cs
index 24b63ca..15d8410 100644
--- a/Main/System/Tip/ScrollTip.cs
+++ b/Main/System/Tip/ScrollTip.cs
@@ -75,7 +75,7 @@
         if (pool == null)
         {
             var _prefab = await UILoader.LoadPrefabAsync("Tip");
-            if (pool != null)
+            if (pool == null)
             {
                 pool = GameObjectPoolManager.Instance.GetPool(_prefab);
             }
diff --git a/Main/Utility/FrameEffect.cs b/Main/Utility/FrameEffect.cs
index c062673..5875539 100644
--- a/Main/Utility/FrameEffect.cs
+++ b/Main/Utility/FrameEffect.cs
@@ -52,7 +52,10 @@
             {
                 if (timer > interval)
                 {
-                    m_Behaviour.overrideSprite = m_Sprites[index];
+                    int newIndex = index;
+                    // 鍙湪sprite瀹為檯鍙樺寲鏃舵墠璧嬪�硷紝閬垮厤涓嶅繀瑕佺殑Image rebuild
+                    if (m_Behaviour.overrideSprite != m_Sprites[newIndex])
+                        m_Behaviour.overrideSprite = m_Sprites[newIndex];
                     index = (++index) % m_Sprites.Length;
                     timer -= interval;
                 }
diff --git a/Main/Utility/UGUIEventListenerContainDrag.cs b/Main/Utility/UGUIEventListenerContainDrag.cs
index 56e7dfd..3f08808 100644
--- a/Main/Utility/UGUIEventListenerContainDrag.cs
+++ b/Main/Utility/UGUIEventListenerContainDrag.cs
@@ -28,12 +28,28 @@
     //鏄惁澶勪簬鎸変笅鐘舵�� 涓庨暱鎸夐厤鍚堜娇鐢�
     bool isDown = false;
     float time = 0;
+    
+    private Button _cachedButton;
+    private bool _buttonCached;
+    
+    private Button CachedButton
+    {
+        get
+        {
+            if (!_buttonCached)
+            {
+                _cachedButton = GetComponent<Button>();
+                _buttonCached = true;
+            }
+            return _cachedButton;
+        }
+    }
 
     public void OnPointerClick(PointerEventData eventData)
     {
         if (OnClick != null)
         {
-            if (!GetComponent<Button>() || GetComponent<Button>().interactable)
+            if (!CachedButton || CachedButton.interactable)
             {
                 OnClick(gameObject);
             }
diff --git a/Main/Utility/UIHelper.cs b/Main/Utility/UIHelper.cs
index 92cdf18..0c20d9b 100644
--- a/Main/Utility/UIHelper.cs
+++ b/Main/Utility/UIHelper.cs
@@ -1498,10 +1498,10 @@
                 serverList.Add(serverName);
                 //澶暱浼氬鑷寸晫闈㈤《鐐规暟瓒呰繃65000
                 if (serverList.Count > 1000)
-                    return string.Join(", ", serverList.ToArray());
+                    return string.Join(", ", serverList);
             }
         }
-        return string.Join(", ", serverList.ToArray());
+        return string.Join(", ", serverList);
     }
 
     //涓嶅悓鐗堟湰鐜伴噾鐨勫崟浣嶄笉涓�鏍凤紝姣斿瓒婂崡鐩炬槸鏁存暟锛屼笅鍙戞槸鍘熷�硷紱缇庡厓鍜孯MB鏄皬鏁帮紝涓嬪彂鏄師鍊肩殑100
diff --git a/Main/Utility/WaitForSecondsCache.cs b/Main/Utility/WaitForSecondsCache.cs
new file mode 100644
index 0000000..2a17a2f
--- /dev/null
+++ b/Main/Utility/WaitForSecondsCache.cs
@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+/// <summary>
+/// WaitForSeconds缂撳瓨锛岄伩鍏嶆瘡娆ield鏃跺垎閰岹C
+/// 浣跨敤鏂瑰紡锛歽ield return WaitForSecondsCache.Get(1f);
+/// </summary>
+public static class WaitForSecondsCache
+{
+    private static readonly Dictionary<float, WaitForSeconds> _cache = new Dictionary<float, WaitForSeconds>();
+
+    public static WaitForSeconds Get(float seconds)
+    {
+        if (!_cache.TryGetValue(seconds, out var wait))
+        {
+            wait = new WaitForSeconds(seconds);
+            _cache[seconds] = wait;
+        }
+        return wait;
+    }
+
+    // 棰勭紦瀛樺父鐢ㄥ��
+    public static readonly WaitForSeconds Wait0_1 = new WaitForSeconds(0.1f);
+    public static readonly WaitForSeconds Wait0_5 = new WaitForSeconds(0.5f);
+    public static readonly WaitForSeconds Wait1 = new WaitForSeconds(1f);
+    public static readonly WaitForSeconds Wait2 = new WaitForSeconds(2f);
+    public static readonly WaitForSeconds Wait5 = new WaitForSeconds(5f);
+}
diff --git a/Main/Utility/WaitForSecondsCache.cs.meta b/Main/Utility/WaitForSecondsCache.cs.meta
new file mode 100644
index 0000000..8648081
--- /dev/null
+++ b/Main/Utility/WaitForSecondsCache.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: de580f55531ab54439b4e279b87951e7
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

--
Gitblit v1.8.0