From 0ebbc9c3413b6862b0723add5d5cad51958d7d48 Mon Sep 17 00:00:00 2001
From: Ding Yuntian <1491671119@qq.com>
Date: Thu, 26 Mar 2026 16:08:41 +0800
Subject: [PATCH] =?UTF-8?q?=EF=BB=BFfix(resource-kit):=20=E4=BF=AE?=
=?UTF-8?q?=E5=A4=8D=E5=9C=BA=E6=99=AF=E8=B5=84=E6=BA=90=E5=8A=A0=E8=BD=BD?=
=?UTF-8?q?=E5=99=A8=E7=94=9F=E5=91=BD=E5=91=A8=E6=9C=9F=E4=B8=8E=E5=88=9D?=
=?UTF-8?q?=E5=A7=8B=E5=8C=96=E6=97=B6=E5=BA=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Made-with: Cursor
---
.../FixSystemPrefabs/Fix System Center.prefab | 14 ----
.../BodyModule/Modules/BodyModule.cs | 4 +
.../Framework/ResourceKit/ResourceSystem.cs | 77 +++++++++++--------
.../ResourceKit/SceneResourceLoader.cs | 21 ++---
Assets/Scripts/Game Loop/SceneLoader.cs | 25 ++----
Docs/ResourceLoadingBestPractices.md | 8 +-
6 files changed, 70 insertions(+), 79 deletions(-)
diff --git a/Assets/Prefabs/FixSystemPrefabs/Fix System Center.prefab b/Assets/Prefabs/FixSystemPrefabs/Fix System Center.prefab
index 96a0d4bb0..62a2b4047 100644
--- a/Assets/Prefabs/FixSystemPrefabs/Fix System Center.prefab
+++ b/Assets/Prefabs/FixSystemPrefabs/Fix System Center.prefab
@@ -10,7 +10,6 @@ GameObject:
m_Component:
- component: {fileID: 4256475766480134775}
- component: {fileID: 667196269251774960}
- - component: {fileID: 9119428735370279783}
m_Layer: 0
m_Name: Fix System Center
m_TagString: Untagged
@@ -46,16 +45,3 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
defaultState: 1
---- !u!114 &9119428735370279783
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 422366950737630991}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: ef81dc021856ce1458b39c19b69cfc9b, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- isPersistent: 0
diff --git a/Assets/Scripts/FixSystemNew/BodyModule/Modules/BodyModule.cs b/Assets/Scripts/FixSystemNew/BodyModule/Modules/BodyModule.cs
index 2e8024e0e..6e0eb2033 100644
--- a/Assets/Scripts/FixSystemNew/BodyModule/Modules/BodyModule.cs
+++ b/Assets/Scripts/FixSystemNew/BodyModule/Modules/BodyModule.cs
@@ -46,6 +46,10 @@ namespace AibisDream.FixSystem
{
// 获取组件索引
InitReference();
+ }
+
+ private void Start()
+ {
StartCoroutine(LoadMaterial());
}
diff --git a/Assets/Scripts/Framework/ResourceKit/ResourceSystem.cs b/Assets/Scripts/Framework/ResourceKit/ResourceSystem.cs
index 406ee2f65..dbd3a22ee 100644
--- a/Assets/Scripts/Framework/ResourceKit/ResourceSystem.cs
+++ b/Assets/Scripts/Framework/ResourceKit/ResourceSystem.cs
@@ -15,37 +15,46 @@ namespace AibisDream.Framework
private static SceneResourceLoader _persistentLoader;
private static SceneResourceLoader _sceneLoader;
- #region Loader Registration
+ #region Loader Management
- public static void RegisterPersistentLoader(SceneResourceLoader loader)
+ ///
+ /// 创建场景级 Loader。由 SceneLoader 在场景加载完成后调用。
+ ///
+ public static void CreateSceneLoader()
{
- if (_persistentLoader != null && _persistentLoader != loader)
- Debug.LogWarning("[ResourceSystem] Replacing existing persistent loader.");
- _persistentLoader = loader;
+ if (_sceneLoader != null)
+ {
+ Debug.LogWarning("[ResourceSystem] Previous scene loader exists, releasing it.");
+ _sceneLoader.Dispose();
+ }
+ _sceneLoader = new SceneResourceLoader(isPersistent: false);
+ Debug.Log("[ResourceSystem] Scene loader created.");
}
- public static void UnregisterPersistentLoader(SceneResourceLoader loader)
+ ///
+ /// 释放场景级 Loader。由 SceneLoader 在场景卸载前调用。
+ ///
+ public static void ReleaseSceneLoader()
{
- if (_persistentLoader == loader)
- _persistentLoader = null;
- }
-
- public static void RegisterSceneLoader(SceneResourceLoader loader)
- {
- if (_sceneLoader != null && _sceneLoader != loader)
- Debug.LogWarning("[ResourceSystem] Replacing existing scene loader. Was the previous scene properly unloaded?");
- _sceneLoader = loader;
- }
-
- public static void UnregisterSceneLoader(SceneResourceLoader loader)
- {
- if (_sceneLoader == loader)
+ if (_sceneLoader != null)
+ {
+ int releasedCount = _sceneLoader.TotalHandleCount;
+ _sceneLoader.Dispose();
_sceneLoader = null;
+ Debug.Log($"[ResourceSystem] Scene loader released. {releasedCount} handles released.");
+ }
}
- public static void UnregisterSceneLoader()
+ ///
+ /// 懒加载 Persistent Loader。首次调用 LoadPersistentAsync 时自动创建。
+ ///
+ private static void EnsurePersistentLoader()
{
- _sceneLoader = null;
+ if (_persistentLoader == null)
+ {
+ _persistentLoader = new SceneResourceLoader(isPersistent: true);
+ Debug.Log("[ResourceSystem] Persistent loader created (lazy).");
+ }
}
#endregion
@@ -61,7 +70,7 @@ namespace AibisDream.Framework
{
if (_sceneLoader != null) return _sceneLoader;
if (_persistentLoader != null) return _persistentLoader;
- Debug.LogError("[ResourceSystem] No loader registered. Ensure a SceneResourceLoader exists in the scene.");
+ Debug.LogError("[ResourceSystem] No loader available. Ensure SceneLoader.CreateSceneLoader() was called.");
return null;
}
}
@@ -82,14 +91,14 @@ namespace AibisDream.Framework
public static AsyncOperationHandle LoadPersistentAsync(string key) where T : Object
{
- EnsureLoader(_persistentLoader, "persistent");
+ EnsurePersistentLoader();
return _persistentLoader.LoadAsync(key);
}
public static void LoadPersistentAsync(string key, Action onComplete, Action onError = null)
where T : Object
{
- EnsureLoader(_persistentLoader, "persistent");
+ EnsurePersistentLoader();
_persistentLoader.LoadAsync(key, onComplete, onError);
}
@@ -99,14 +108,22 @@ namespace AibisDream.Framework
public static AsyncOperationHandle LoadSceneAsync(string key) where T : Object
{
- EnsureLoader(_sceneLoader, "scene");
+ if (_sceneLoader == null)
+ {
+ Debug.LogError("[ResourceSystem] No scene loader. Ensure CreateSceneLoader() was called by SceneLoader.");
+ return default;
+ }
return _sceneLoader.LoadAsync(key);
}
public static void LoadSceneAsync(string key, Action onComplete, Action onError = null)
where T : Object
{
- EnsureLoader(_sceneLoader, "scene");
+ if (_sceneLoader == null)
+ {
+ Debug.LogError("[ResourceSystem] No scene loader. Ensure CreateSceneLoader() was called by SceneLoader.");
+ return;
+ }
_sceneLoader.LoadAsync(key, onComplete, onError);
}
@@ -124,11 +141,5 @@ namespace AibisDream.Framework
=> ActiveLoader.EarlyRelease(handle);
#endregion
-
- private static void EnsureLoader(SceneResourceLoader loader, string loaderName)
- {
- if (loader == null)
- Debug.LogError($"[ResourceSystem] No {loaderName} loader registered.");
- }
}
}
diff --git a/Assets/Scripts/Framework/ResourceKit/SceneResourceLoader.cs b/Assets/Scripts/Framework/ResourceKit/SceneResourceLoader.cs
index 1d95ccbd8..b0ebe7486 100644
--- a/Assets/Scripts/Framework/ResourceKit/SceneResourceLoader.cs
+++ b/Assets/Scripts/Framework/ResourceKit/SceneResourceLoader.cs
@@ -7,31 +7,24 @@ using Object = UnityEngine.Object;
namespace AibisDream.Framework
{
- public class SceneResourceLoader : MonoBehaviour
+ public class SceneResourceLoader : IDisposable
{
- [SerializeField] private bool isPersistent;
-
+ private readonly bool _isPersistent;
private readonly Dictionary _cache = new();
private readonly List _uncachedHandles = new();
- private void Awake()
+ public SceneResourceLoader(bool isPersistent = false)
{
- if (isPersistent)
- ResourceSystem.RegisterPersistentLoader(this);
- else
- ResourceSystem.RegisterSceneLoader(this);
+ _isPersistent = isPersistent;
}
- private void OnDestroy()
+ public void Dispose()
{
ReleaseAll();
-
- if (isPersistent)
- ResourceSystem.UnregisterPersistentLoader(this);
- else
- ResourceSystem.UnregisterSceneLoader(this);
}
+ public bool IsPersistent => _isPersistent;
+
///
/// 异步加载资源(带缓存,同 Key 不重复加载)。
/// 返回的 Handle 可在协程中 yield return。
diff --git a/Assets/Scripts/Game Loop/SceneLoader.cs b/Assets/Scripts/Game Loop/SceneLoader.cs
index 04c079639..6e1bddf22 100644
--- a/Assets/Scripts/Game Loop/SceneLoader.cs
+++ b/Assets/Scripts/Game Loop/SceneLoader.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections;
using AibisDream.Framework;
using AibisDream.Kit;
@@ -65,19 +65,19 @@ namespace AibisDream
FixSystemCenter.SystemDic.Clear();
}
- // 释放场景级资源(在卸载场景之前,避免 OnDestroy 顺序问题)
- ResourceSystem.CurrentSceneLoader?.ReleaseAll();
- ResourceSystem.UnregisterSceneLoader();
-
+ // 释放场景级资源(在卸载场景之前)
+ ResourceSystem.ReleaseSceneLoader();
+
yield return Addressables.UnloadSceneAsync(_loadHandle);
}
- yield return PreloadSceneAssets(_sceneToLoad);
+ // 显式创建 Scene Loader(在所有业务组件 Start 之前)
+ ResourceSystem.CreateSceneLoader();
_loadHandle = Addressables.LoadSceneAsync(_sceneToLoad, LoadSceneMode.Additive);
yield return _loadHandle;
- // 新场景中的 SceneResourceLoader 会在 Awake 中自动注册到 ResourceSystem
+ // 场景加载完成,通知各系统
EnumEventSystem.Global.Send(EventEnum.SceneLoad, _sceneToLoad);
_data.sceneName = _sceneToLoad;
UIManager.Instance.GetPanel().HideLoading();
@@ -97,8 +97,7 @@ namespace AibisDream
if (_loadHandle.IsValid())
{
- ResourceSystem.CurrentSceneLoader?.ReleaseAll();
- ResourceSystem.UnregisterSceneLoader();
+ ResourceSystem.ReleaseSceneLoader();
yield return Addressables.UnloadSceneAsync(_loadHandle);
_data.sceneName = null;
@@ -107,14 +106,6 @@ namespace AibisDream
_isLoading = false;
}
}
-
- ///
- /// 预加载目标场景所需的资源。后续 Label 配置完成后在此处填充具体逻辑。
- ///
- private IEnumerator PreloadSceneAssets(string targetScene)
- {
- yield break;
- }
}
[Serializable]
diff --git a/Docs/ResourceLoadingBestPractices.md b/Docs/ResourceLoadingBestPractices.md
index 9ab11cfe0..41f1c372e 100644
--- a/Docs/ResourceLoadingBestPractices.md
+++ b/Docs/ResourceLoadingBestPractices.md
@@ -58,7 +58,13 @@ Timeline 等大资源应使用 `LoadUncachedAsync` 加载,播放完后通过 `
### 2.2 注意事项
-**ActiveLoader 回退风险**:`LoadAsync`(自动路由)在 Scene Loader 不存在时会静默回退到 Persistent Loader。这意味着在场景切换间隙(旧场景已卸载、新场景 `SceneResourceLoader.Awake` 尚未执行)调用 `LoadAsync`,资源会被加载到 Persistent Loader 中,**永远不会随场景卸载释放**。避免在此时间窗口内调用 `LoadAsync`;若有明确场景归属,优先使用 `LoadSceneAsync` 或 `LoadPersistentAsync`。
+**禁止在 Awake 中加载资源**:`SceneResourceLoader` 在 `Awake` 中注册自身到 `ResourceSystem`,而 Unity 的 `Awake` 执行顺序是不确定的。如果在另一个组件的 `Awake` 中调用 `ResourceSystem.LoadAsync`,此时当前场景的 `SceneResourceLoader` 可能尚未完成注册,`ResourceSystem` 会因找不到 Scene Loader 而回退到 Persistent Loader 加载资源。这会导致:
+ 1) 资源被错误地缓存到 Persistent Loader,永远不会随场景卸载释放;
+ 2) 后续在协程中再次加载同 Key 资源时,由于缓存命中,返回的是 Persistent Loader 中的 Handle,进一步掩盖问题。
+
+ **解决方案**:所有资源加载必须在协程(如 `Start` 或 `IEnumerator` 流程)中执行,确保 `SceneResourceLoader.Awake()` 已完成执行。
+
+- **ActiveLoader 回退风险**:`LoadAsync`(自动路由)在 Scene Loader 不存在时会静默回退到 Persistent Loader。这意味着在场景切换间隙(旧场景已卸载、新场景 `SceneResourceLoader.Awake` 尚未执行)调用 `LoadAsync`,资源会被加载到 Persistent Loader 中,**永远不会随场景卸载释放**。避免在此时间窗口内调用 `LoadAsync`;若有明确场景归属,优先使用 `LoadSceneAsync` 或 `LoadPersistentAsync`。
**同一 Key 只能以一种类型加载**:`SceneResourceLoader` 的缓存以 Key 字符串为索引。如果先调用 `LoadAsync(key)` 再调用 `LoadAsync(key)`,第二次会返回已缓存的 Handle 并尝试类型转换,可能导致 `InvalidCastException`。确保同一 Key 在整个场景生命周期内只以一种类型加载。