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 在整个场景生命周期内只以一种类型加载。