@[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;
}