@[TOC](【Unity开发】避免重复加载场景资产AB(AssetBundle)包的优化)
项目需要远程加载场景AB包,并加载场景。
1、 初始方案
1.1 异步加载场景资产(获取AB包数据)
public IEnumerator LoadAssetFromAB(string url, Action<AssetBundle> assetAB){yield return new WaitForSeconds(0.16f);using (UnityWebRequest webRequest = UnityWebRequestAssetBundle.GetAssetBundle(url)){yield return webRequest.SendWebRequest();if (webRequest.result == UnityWebRequest.Result.Success){var bundle = DownloadHandlerAssetBundle.GetContent(webRequest);assetAB?.Invoke(bundle);}}}
1.2 异步加载场景(进入场景)
// 异步加载远程AB场景public void LoadSceneFromAB(AssetBundle assetAB, string sceneName){// 启动异步线程加载场景StartCoroutine(AssetToScene(assetAB, sceneName));}// 异步远程AB场景迭代器private IEnumerator AssetToScene(AssetBundle assetAB, string sceneName){// 异步加载场景资产AsyncOperation loadOperation = SceneManager.LoadSceneAsync(sceneName);yield return loadOperation;Debug.Log("应该到不了这里就直接转场了,那么AB资产包还在内存,下次就会报[重复加载]的异常");// 卸载内存中的assetAB资产(除了LoadScene创建出来的场景)assetAB.Unload(false);}
1.3 点击跳转场景
public void GotoScene(ClickEvent evt){string sceneName = "myScene";char dsp = Path.AltDirectorySeparatorChar;string url = Application.dataPath + dsp + "scene" + dsp + sceneName;// 销毁所有未使用资产Resources.UnloadUnusedAssets();// 临时解决方案// 尝试直接加载AB场景 (避免AB资产已在内存时AB包重复报错)// The AssetBundle 'http://192.168.2.107:8080/level/Level1' can't be loaded because another AssetBundle with the same files is already loaded.SceneManager.LoadScene(sceneName);// 这里如果成功加载场景,不会走到下一步,// 前提是内存里有场景包 1、上次已经加载到内存没卸载 2、程序Build编译时Scenes In Build// Scene 'Level1' couldn't be loaded because it has not been added to the build settings or the AssetBundle has not been loaded.//To add a scene to the build settings use the menu File->Build Settings...// 远程异步加载AB场景资产StartCoroutine(_dataRestapi.LoadAssetFromAB(url, (assetAB) => {if (assetAB != null) {LoadSceneFromAB(assetAB, sceneName);}else{Debug.LogWarning("场景资产数据加载失败");}}));}
2、 优化方案
2.1 异步加载场景资产(获取AB包数据)
public IEnumerator LoadAssetFromAB(string url, Action<AssetBundle> assetAB){yield return new WaitForSeconds(0.16f);using (UnityWebRequest webRequest = UnityWebRequestAssetBundle.GetAssetBundle(url)){yield return webRequest.SendWebRequest();if (webRequest.result == UnityWebRequest.Result.Success){var bundle = DownloadHandlerAssetBundle.GetContent(webRequest);assetAB?.Invoke(bundle);}}}
2.2 异步加载场景(进入场景)
1 注册后处理事件 SceneManager.sceneLoaded += OnSceneLoaded;
2 删除永远不会被运行的 assetAB.Unload(false);
// 异步加载远程AB场景public void LoadSceneFromAB(AssetBundle assetAB, string sceneName){// 启动异步线程加载场景StartCoroutine(AssetToScene(assetAB, sceneName));// 当新场景开始加载时注册事件 解决加载完成后处理事件SceneManager.sceneLoaded += OnSceneLoaded;}// 异步远程AB场景迭代器private IEnumerator AssetToScene(AssetBundle assetAB, string sceneName){// 异步加载场景资产AsyncOperation loadOperation = SceneManager.LoadSceneAsync(sceneName);yield return loadOperation;}
2.3 点击跳转场景
删除尝试加载场景 SceneManager.LoadScene(sceneName);
因为不需要了,每次运行都是从远程获取最新的场景AB包
public void GotoScene(ClickEvent evt){string sceneName = "myScene";char dsp = Path.AltDirectorySeparatorChar;string url = Application.dataPath + dsp + "scene" + dsp + sceneName;// 销毁所有未使用资产Resources.UnloadUnusedAssets();// 远程异步加载AB场景资产StartCoroutine(_dataRestapi.LoadAssetFromAB(url, (assetAB) => {if (assetAB != null) {LoadSceneFromAB(assetAB, sceneName);}else{Debug.LogWarning("场景资产数据加载失败");}}));}
2.4 增加场景加载后处理事件
保证场景加载完成后自动删除场景AB资产包
// 场景加载后处理事件void OnSceneLoaded(Scene scene, LoadSceneMode mode){// 当新场景加载完成后,卸载内存中AssetBundle包(除了Load创建出来的对象/场景)_assetAB.Unload(false);// 注销事件,防止重复调用SceneManager.sceneLoaded -= OnSceneLoaded;}