为什么要使用协程加载AB包?
因为协程能够实现等待资源异步加载完毕后,再同步处理后续的代码逻辑。也就是说在代码逻辑上是同步,但实际是异步加载的方式,能够保证功能的完整性。
/**
加载UI面板
*/
public IEnumerator loadUIByName(string uiname)
{
//加载ab包资源及其依赖的资源
string assetBundleName = getUIAssetBundleName(uiname);
yield return loadAssetBundleWithDependienies(assetBundleName);
//资源加载完成后,实例化UI对象
LoadedAssetBundle loadedAssetBundle = getLoadedAssetBundle(assetBundleName);
AssetBundle assetBundle = loadedAssetBundle.assetBundle;
if (assetBundle != null)
{
GameObject go = assetBundle.LoadAsset<GameObject>(uiname);
GameObject.Instantiate(go);
go.name = uiname;
if (!go.activeSelf)
go.SetActive(true);
if(!showInternal(uiname, loadedAssetBundle))
{
Debug.Log("showUI:" + uiname + "Error");
}
}
}
在加载ab包的同时,我们需要加载它相关的依赖包,这个依赖包的数据在manifest文件中有记录,通常会直接读取主包的manifest文件,这里面记录了所有包及其依赖信息,所以在加载依赖包之前需要先加载主包数据。
//加载主包
private void loadMianAB()
{
if (mainBundle == null)
{
mainBundle = AssetBundle.LoadFromFile(ROOT_PATH + "AssetBundles");
mainBundleManifest = mainBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
}
}
主包加载后,通过mainBundleManifest.GetAllDependencies(path),可以读取该包所有的依赖包,这些依赖包也都需要加载。当然还会存在依赖包中还有依赖包,这种情况可以使用递归的方式处理,这里就暂时不考虑。
/**
加载ab包资源及其所依赖的ab包
*/
public bool loadAssetBundleWithDependienies(string assetBundleName)
{
loadAssetBundleAsyn(assetBundleName);
//加载依赖包
string[] dependencies = null;
string relativePath = assetBundleName;
dependencies = mainBundleManifest.GetAllDependencies(relativePath);
foreach (string dependency in dependencies)//遍历依赖包名,加载
{
loadAssetBundleAsyn(dependency);
}
return true;
}
ab包的重复加载会报错,所以需要一个字典保存所有加载过的ab包,在加载之前还需要判断该包是否已经加载过,若已加载直接从字典中取,未加载则开启协程加载。
//重复加载报错
The AssetBundle 'player.unity3d' can't be loaded because another AssetBundle with the same files is already loaded.
/**
开启协程异步加载ab包资源,若已加载则从缓存中取
*/
public bool loadAssetBundleAsyn(string abName)
{
LoadedAssetBundle bundle = null;
loadedAssetBundles.TryGetValue(abName, out bundle);
if (bundle != null)
{
bundle.referencedCount++;
return true;
}
StartCoroutine(loadSingleAssetBundle(abName));
return true;
}
开启协程加载单个ab包,使用AssetBundle.LoadFromFileAsync(path)异步的方式加载,协程等待加载完成。
/**
异步加载单个ab包资源,加载完成后,缓存进已加载字典中
*/
private IEnumerator loadSingleAssetBundle(string abName)
{
LoadingAssetInfo info = new LoadingAssetInfo();
info.path = ROOT_PATH + abName;
info.abReq = AssetBundle.LoadFromFileAsync(info.path);
yield return info.isDone;
//Debug.Log("协程加载" + abName + "完成");
loadedAssetBundles.Add(abName, new LoadedAssetBundle(info));
assetBundleNames.Add(abName);
}
这里的loadingAssetInfo封装了加载信息:是否完成、进度、错误、引用等,方便以后扩展成www的加载方式。loadedAssetBundle封装了ab包引用,计数引用,卸载等方法。这些都可以根据自己的需要灵活使用。
总结一下加载的过程,首先开启主协程进行加载,在主协程中开启多个分支协程,进行异步加载单个ab包资源,等到所有的分支协程都已完成异步加载后,主协程也完成了使命,执行后续打开界面的流程。