1.简介
AB包(AssetBundle)是Unity中用于资源管理的一种机制,它允许开发者将多个文件(如纹理、模型、音频等)打包成一个单独的文件,以便于在游戏运行时动态加载和卸载。 但是现在出现了最新的Addressable来代替AssetBundle.
Addressables是Unity提供的一个资源管理系统,它是对AssetBundle系统的一种改进和扩展。Addressables允许开发者通过地址(名称或标签)来请求资源,而不是直接操作AssetBundle文件。这使得资源的管理和加载变得更加简单和灵活。现在目前只接收AssetBundle的用法个管理器的代码
2.特点
-
资源优化:通过将相关资源打包在一起,可以减少文件数量,简化资源管理。
-
减少启动时间:游戏开始时不需要加载所有资源,只需加载必要的AB包,从而减少启动时间。
-
动态内容更新:可以单独更新AB包中的内容,而不需要重新下载整个游戏。
3.工作流程
AB包的基本工作流程包括以下几个步骤:
- 构建AB包:在Unity编辑器中,将需要打包的资源分配到相应的AB包中,并设置好它们的名称和变体。
- 打包:使用Unity的构建系统将资源打包成AB包文件。这些文件通常具有
.unity3d
扩展名。我们类自己命名都是可以的 - 加载AB包:在游戏运行时,通过代码动态加载所需的AB包。可以使用
AssetBundle.LoadFromFile
或AssetBundle.LoadFromMemory
等方法。 - 使用资源:从AB包中加载具体的资源,如纹理、预制体等。
- 卸载AB包:当AB包中的资源不再需要时,应该卸载AB包以释放内存。
在使用AB包时,需要注意资源依赖关系的管理,确保在加载资源时,其依赖的所有资源都已正确加载。此外,AB包的版本控制和更新策略也是开发过程中需要考虑的重要方面。
4.AB包管理器
为了管理ab包, 在需要的时候加载ab包, 因此,我们需要建立一个AB包的管理器, 该管理器提供了加载AB包的方式, 为了方便使用者, 可以通过该管理器直接快速加载某AB包里的某个资源.
4.1 效果图和部分说明
以下是AB包加载效果
通过AssetBundleBrowerk可以查看到打的ab包
说明:
- textures.ab包中包含唯一一张贴图, T_Wood.jpg
- materials.ab包中含有两个材质, 分别是M_WineWood和M_Wood, 两个材质都引用的同一张贴图W_Wood.jpg.
- static.ab包中含有两个预制体, wall和floor. wall预制体中的材质是M_Wood, floor中使用的M_WineMood.
依赖关系如下图所示.
其中,在主包中包含3个包, 分别是texture.ab 存放贴图, materials.ab存放材质, static.ab存放预制体. 依赖关系如如下图. 下图中的右图是简化版本. 左图中显示了包与包的依赖关系, 右图展示了贴图-材质-预制体(模型)的关系.
根据AssestBundle的加载资源包(ab包)的规则, 如果你想要加载一个资源包,需要先加载该资源包的依赖包,
如果不先加载依赖包,那么运行的效果可能就是丢失了材质/贴图/脚本或者其他资源导致程序出现异常或者崩溃.
因此, 理清楚依赖关系非常重要. 依赖关系可以在主包中的mainfest文件中获得. 依赖关系就是上图中的线条
所以, 加载顺序为: 加载主包->读取依赖关系->加载依赖包->加载目标包.
在一些很大的游戏中,资源包很大, 因此如果加载资源包等待的时间很长这会导致玩家失去耐心, 和何况在这个短平快的时代. 因此可以根据资源包的大小来进行同步加载
和异步加载
. 同理, 加载完AB包后, 里面的资源可能很大,可能很小, 这需要根据实际来同步加载资源
和异步加载资源
.
以下代码是AB包管理器, 用于加载AB包, 其中也包含了加载资源的同步方式和异步方式. 下图是ABManager.cs
的代码结构.
以下是具体代码:
4.2准备工作
// AB包管理器的目的: 让外部更方便的进行资源加载
// 主包:
private AssetBundle mainAB = null;
// 依赖包获取用的配置文件
private AssetBundleManifest manifest = null;
// 需要将已加载的AB包存起来, 使用字典存起来
// key->ab包的名称 value->包体
Dictionary<string, AssetBundle> abDic = new Dictionary<string, AssetBundle>();
4.3 加载AB包的路径以及平台
// 加载包的路径
private string PathURL
{
get
{
// 可以根据平台不用返回不同的值 streamingAssetsPath 是用于测试
// 注意: AssetsBundle打包的时候试注意路径名称和文件夹名称, 因为主包名称会被构建为最后一层的路径名称
return Application.streamingAssetsPath + "/" + MainABName + "/";
}
}
// 主包名: 区分不同的平台,Unity根据环境自动区分, 后期根据目标平台构建AB包
private string MainABName
{
get
{
#if UNITY_IOS
return "IOS";
#elif UNITY_ANDROID
return "Android"
#else
return "PC";
#endif
}
}
4.4 同步加载AB包
步骤: 加载主包->加载Manifest文件->加载依赖包->加载目标包
使用AssetBundle.LoadFromFile
加载主包/依赖包/目标包,
使用LoadAsset<AssetBundleManifest>("AssetBundleManifest");
加载Mainfest文件获依赖关系
// 同步加载AB包
public AssetBundle LoadAssetBundle(string abName)
{
if (abName == "")
{
Debug.LogError("请输入ab包名称");
return null;
}
// 加载主包 单独保存起来
// 加载主包中的关键配置文件, 获取依赖包 单独保存起来
// 加载主包和Manifes信息
if (mainAB == null)
{
// 路径 + 主包名
mainAB = AssetBundle.LoadFromFile(PathURL + MainABName);
// 加载包与包之间的依赖文件
manifest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
}
// 获取依赖关系:依赖包
AssetBundle ab = null;
if (manifest != null)
{
// 遍历mainfest文件, 获取依赖包 获取所有依赖信息
string[] dependencies = manifest.GetAllDependencies(abName);
for (int i = 0; i < dependencies.Length; ++i)
{
// 如果现有的容器中没有需要加载的包, 则进行加载
if (!abDic.ContainsKey(dependencies[i]))
{
// 根据依赖信息加载依赖包 -- 暂时用不上crc循环冗余校验码
ab = AssetBundle.LoadFromFile(PathURL + dependencies[i]);
// 添加到容器中
abDic.Add(dependencies[i], ab);
}
}
}
// 加载目标包 加载完依赖包后系统不会自动加载最终我们要的包, 因此需要手动加载
if (!abDic.ContainsKey(abName))
{
ab = AssetBundle.LoadFromFile(PathURL + abName);
abDic.Add(abName, ab);
}
else
{
ab = abDic[abName];
}
// 返回资源包
return ab;
}
4.5 异步加载资源包
由于异步加载资源包需要配合协程进行操作, 因此在加载资源包的时候需要开启协程. 同时需要注意协程返回数据的时间, 因为每一个包的大小不一样, 因此同时开启多个协程的时候需要注意一些临界值.
public void LoadAssetBundleAsync(string abName, UnityAction<AssetBundle> callback)
{
// 开启协程加载AB包
StartCoroutine(ReallyLoadAssetBundle(abName, callback));
}
// 协程加载AB包
public IEnumerator ReallyLoadAssetBundle(string abName, UnityAction<AssetBundle> callback)
{
if (abName == null || abName == "")
{
Debug.LogError("加载的AB包不能为空");
yield break;
}
// 先判断是否在abDic中
if (abDic.ContainsKey(abName))
{
yield return abDic[abName]; // 这句可以注释掉
callback(abDic[abName]);
yield break;
}
else
{
// 不在abDic中, 加载主包 和Manifest文件
// 加载主包和Manifest信息
if (mainAB == null)
{
// 路径 + 主包名
AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(PathURL + MainABName);
yield return abcr;
mainAB = abcr.assetBundle;
// 加载包与包之间的依赖文件
AssetBundleRequest abr = mainAB.LoadAssetAsync<AssetBundleManifest>("AssetBundleManifest");
yield return abr;
manifest = abr.asset as AssetBundleManifest;
}
// 遍历依赖包
string[] dependencies = manifest.GetAllDependencies(abName);
// 这里使用队列, 是因为多个协程的返回时间不一样, 如果直接使用abDic.Add的方法会导致异常, 即for循环中的i发生改变,, 因此先用队列缓存起来.
// 另外的做法是, 先将名称放入abDic字典中, 值用Null来代替, 加载完毕后再修改abDic中的值
Queue<AssetBundle> loadingQueue = new Queue<AssetBundle>(dependencies.Length);
for (int i = 0; i < dependencies.Length; i++)
{
// 开启单个依赖包的加载协程
StartCoroutine(LoadSingleDependenceAsync(dependencies[i], (temp) =>
{
// 加载完毕后入队
loadingQueue.Enqueue(temp);
}));
}
// 依赖包加载完毕后加载目标包-->开启加载目标包的协程
StartCoroutine(LoadTargetAssetBundleAsync(abName, (temp) => {
// 等待所有依赖包加载完成
// 出队, 添加到abDic中
while (loadingQueue.Count > 0)
{
AssetBundle loadedAB = loadingQueue.Dequeue();
abDic.Add(loadedAB.name, loadedAB);
}
callback(temp);
}));
}
}
// 依赖包的加载 : 依赖包名称, 路径, 加载完毕的回调函数
public IEnumerator LoadSingleDependenceAsync(string dependName, UnityAction<AssetBundle> callback)
{
AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(PathURL + dependName);
if (abcr.assetBundle != null)
{
yield return abcr;
callback(abcr.assetBundle);
}
else
{
Debug.LogError("单个依赖包加载出错");
callback(null);
yield break;
}
}
// 加载目标包的协程
public IEnumerator LoadTargetAssetBundleAsync(string abName, UnityAction<AssetBundle> callback)
{
AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(PathURL + abName);
if (abcr.assetBundle != null)
{
yield return abcr;
callback(abcr.assetBundle);
}
else
{
Debug.LogError("目标包加载出错");
callback(null);
yield break;
}
}
4.6 同步加载资源的三种方式
/// <summary>
/// 同步加载
/// </summary>
/// <typeparam name="T">通过泛型加载</typeparam>
/// <param name="abName">资源包的名称</param>
/// <param name="resName">资源名称</param>
/// <returns>加载的资源</returns>
public T LoadResources<T>(string abName, string resName) where T : Object
{
AssetBundle ab = LoadAssetBundle(abName);
if (ab == null)
{
return null;
}
// Debug.Log("同步加载-指定泛型的加载模式");
return ab.LoadAsset<T>(resName);
}
// 同步加载 不指定类型
public Object LoadResources(string abName, string resName)
{
// 加载AB包
AssetBundle ab = LoadAssetBundle(abName);
Object obj = ab.LoadAsset(resName);
// Debug.Log("同步加载-不指定类型的加载模式");
return obj;
}
/// <summary>
/// 使用资源类型的方式进行加载 xLua中不支持泛型所以使用type类型
/// </summary>
/// <param name="abName">资源包的名称</param>
/// <param name="resName">资源名称</param>
/// <param name="type">资源类型</param>
/// <returns>加载的资源</returns>
// 同步加载 根据type指定类型 这里使用System.Type是因为返回的对象Object在Unity和System中均有Object.
public Object LoadResources(string abName, string resName, System.Type type)
{
// 加载AB包
AssetBundle ab = LoadAssetBundle(abName);
Object obj = ab.LoadAsset(resName, type);
// Debug.Log("同步加载-指定Type的加载模式");
return obj;
}
4.7 异步加载资源的三种方式
异步加载AB后, 异步加载包中的资源
/// <summary>
/// 异步加载资源-启动协程
/// </summary>
public void LoadResourcesAsync(string abName, string resName, UnityAction<Object> callback)
{
// 开启协程
StartCoroutine(ReallyLoadResourcesAsync(abName, resName, callback));
}
// 异步加载资源的协程
public IEnumerator ReallyLoadResourcesAsync(string abName, string resName, UnityAction<Object> callback)
{
LoadAssetBundleAsync(abName, (tmp) =>
{
AssetBundleRequest abr = tmp.LoadAssetAsync(resName);
if (abr == null || abr.asset == null)
{
Debug.LogError("未能加载资源包");
callback(null);
}
else
{
callback(abr.asset);
}
// 得到资源后abr后返回abr的asset
});
yield return resName;
}
/// <summary>
/// 异步加载资源的泛型的方式
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="abName"></param>
/// <param name="resName"></param>
/// <param name="callback"></param>
public void LoadResourcesAsync<T>(string abName, string resName, UnityAction<T> callback) where T : Object
{
// 开启协程
StartCoroutine(ReallyLoadResourcesAsync<T>(abName, resName, callback));
}
// 异步加载资源的协程
public IEnumerator ReallyLoadResourcesAsync<T>(string abName, string resName, UnityAction<T> callback) where T : UnityEngine.Object
{
LoadAssetBundleAsync(abName, (tmp) =>
{
AssetBundleRequest abr = tmp.LoadAssetAsync(resName);
if (abr == null || abr.asset == null)
{
Debug.LogError("未能加载资源包");
callback(null);
}
else
{
callback(abr.asset as T);
}
// 得到资源后abr后返回abr的asset
});
yield return null;
}
public void LoadResourcesAsync(string abName, string resName, System.Type type, UnityAction<Object> callback)
{
// 开启协程
StartCoroutine(ReallyLoadResourcesAsync(abName, resName, type, callback));
}
// 异步加载资源的协程
public IEnumerator ReallyLoadResourcesAsync(string abName, string resName, System.Type type, UnityAction<Object> callback)
{
this.LoadAssetBundleAsync(abName, (tmp) =>
{
AssetBundleRequest abr = tmp.LoadAssetAsync(resName, type);
if (abr == null || abr.asset == null)
{
Debug.LogError("未能加载资源包");
callback(null);
}
else
{
callback(abr.asset);
}
// 得到资源后abr后返回abr的asset
});
yield return null;
}
4.8 AB包卸载
// 单个包的卸载
public void UnloadRes(string abName)
{
if (abDic.ContainsKey(abName))
{
abDic[abName].Unload(false);
abDic.Remove(abName);
}
}
// 所有包的卸载
public void ClearAB()
{
AssetBundle.UnloadAllAssetBundles(false);
abDic.Clear();
mainAB = null;
manifest = null;
}
4.9 完整代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class ABManager : SingletonAutoMono<ABManager>
{
// AB包管理器的目的: 让外部更方便的进行资源加载
// 主包:
private AssetBundle mainAB = null;
// 依赖包获取用的配置文件
private AssetBundleManifest manifest = null;
// 加载分为同步加载异步加载
// AB包加载的特点: 加载后不能再次加载, 因此, 如果作为AB包加载卸载管理器的话
// 需要将已加载的AB包存起来, 使用字典存起来
// key-> ab包的名称 value->包体
Dictionary<string, AssetBundle> abDic = new Dictionary<string, AssetBundle>();
#region 加载AB包的路径以及平台
// 加载包的路径
private string PathURL
{
get
{
// 可以根据平台不用返回不同的值 streamingAssetsPath 是用于测试
// 注意: AssetsBundle打包的时候试注意路径名称和文件夹名称, 应为主包名称会被构建为最后一层的路径名称
return Application.streamingAssetsPath + "/" + MainABName + "/";
}
}
// 主包名: 区分不同的平台
private string MainABName
{
get
{
#if UNITY_IOS
return "IOS";
#elif UNITY_ANDROID
return "Android"
#else
return "PC";
#endif
}
}
#endregion
#region 同步加载AB包
// 同步加载以及同步加载的方法
public AssetBundle LoadAssetBundle(string abName)
{
if(abName == "")
{
Debug.LogError("请输入ab包名称");
return null;
}
// 加载主包 单独保存起来
// 加载主包中的关键配置文件, 获取依赖包 单独保存起来
// 加载主包和Manifes信息
if (mainAB == null)
{
// 路径 + 主包名
mainAB = AssetBundle.LoadFromFile(PathURL + MainABName);
// 加载包与包之间的依赖文件
manifest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
}
// 获取依赖关系:依赖包
AssetBundle ab = null;
if (manifest != null)
{
// 遍历mainfest文件, 获取依赖包 获取所有依赖信息
string[] dependencies = manifest.GetAllDependencies(abName);
for (int i = 0; i < dependencies.Length; ++i)
{
// 如果现有的容器中没有需要加载的包, 则进行加载
if(!abDic.ContainsKey(dependencies[i]))
{
// 根据依赖信息加载依赖包 -- 暂时用不上crc循环冗余校验码
ab = AssetBundle.LoadFromFile(PathURL + dependencies[i]);
// 添加到容器中
abDic.Add(dependencies[i], ab);
}
}
}
// 加载资源来源包
if(!abDic.ContainsKey(abName))
{
ab = AssetBundle.LoadFromFile(PathURL + abName);
abDic.Add(abName, ab);
}else
{
ab = abDic[abName];
}
// 返回资源包
return ab;
}
#endregion
#region 异步加载AB包
public void LoadAssetBundleAsync(string abName, UnityAction<AssetBundle> callback)
{
// 使用协程加载AB包
StartCoroutine(ReallyLoadAssetBundle(abName, callback));
}
public IEnumerator ReallyLoadAssetBundle(string abName, UnityAction<AssetBundle> callback)
{
if(abName == null || abName == "")
{
Debug.LogError("加载的AB包不能为空");
yield break;
}
// 先判断是否在abDic中
if(abDic.ContainsKey(abName))
{
yield return abDic[abName];
callback(abDic[abName]);
yield break;
}else
{
// 不在abDic中, 加载主包 和Manifest文件
// 加载主包和Manifest信息
if (mainAB == null)
{
// 路径 + 主包名
AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync (PathURL + MainABName);
yield return abcr;
mainAB = abcr.assetBundle;
// 加载包与包之间的依赖文件
AssetBundleRequest abr = mainAB.LoadAssetAsync<AssetBundleManifest>("AssetBundleManifest");
yield return abr;
manifest = abr.asset as AssetBundleManifest;
}
// 遍历依赖包
// AssetBundle singleAssetBundle;
string[] dependencies = manifest.GetAllDependencies(abName);
// 这里使用队列作为缓存是为了防止直接使用abDic.Add添加异常
// 如果直接使用abDic.Add会导致下标异常
// 原因是 多个协程加载资源的返回的先后顺序不一致导致for循环导致i值变化, 从而导致abDic的异常
// 另外一种办法是将资源包的name先添加到abDic的key中, value用null占位, 然后在将加载好的资源包放入到abDic的gvalue中
Queue<AssetBundle> loadingQueue = new Queue<AssetBundle>(dependencies.Length);
for (int i = 0; i < dependencies.Length; i++)
{
// 开启单个依赖包的加载协程
StartCoroutine(LoadSingleDependenceAsync(dependencies[i], (temp) =>
{
//singleAssetBundle = temp;
//Debug.Log(singleAssetBundle.name);
loadingQueue.Enqueue(temp);
// 添加到字典中去
//abDic.Add(dependencies[i], singleAssetBundle);
}));
}
// 依赖包加载完毕后加载目标包
StartCoroutine(LoadTargetAssetBundleAsync(abName, (temp) => {
// singleAssetBundle = temp;
// 等待所有依赖包加载完成
while (loadingQueue.Count > 0)
{
AssetBundle loadedAB = loadingQueue.Dequeue();
abDic.Add(loadedAB.name, loadedAB);
}
// abDic.Add(abName, singleAssetBundle);
callback(temp);
}));
}
}
// 依赖包的加载 : 依赖包名称, 路径, 加载完毕的回调函数
public IEnumerator LoadSingleDependenceAsync(string dependName, UnityAction<AssetBundle> callback)
{
AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(PathURL + dependName);
if(abcr.assetBundle != null)
{
yield return abcr;
callback(abcr.assetBundle);
}
else
{
Debug.LogError("单个依赖包加载出错");
callback(null);
yield break;
}
}
// 加载目标包
public IEnumerator LoadTargetAssetBundleAsync(string abName, UnityAction<AssetBundle> callback)
{
AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(PathURL + abName);
if (abcr.assetBundle != null)
{
yield return abcr;
callback(abcr.assetBundle);
}
else
{
Debug.LogError("目标包加载出错");
callback(null);
yield break;
}
}
#endregion
#region 使用泛型方式同步加载资源
/// <summary>
/// 同步加载
/// </summary>
/// <typeparam name="T">通过类型加载</typeparam>
/// <param name="abName">资源包的名称</param>
/// <param name="resName">资源类型</param>
/// <returns></returns>
public T LoadResources<T>(string abName, string resName) where T : Object
{
AssetBundle ab = LoadAssetBundle( abName);
if(ab == null)
{
return null;
}
// Debug.Log("同步加载-指定泛型的加载模式");
return ab.LoadAsset<T>(resName);
}
#endregion
#region 普通方式同步加载资源
//同步加载 不指定类型
public Object LoadResources(string abName, string resName)
{
//加载AB包
AssetBundle ab = LoadAssetBundle(abName);
Object obj = ab.LoadAsset(resName);
// Debug.Log("同步加载-不指定类型的加载模式");
return obj;
}
#endregion
#region 使用type方式同步加载资源
/// <summary>
/// 使用资源类型的方式进行加载 xLua中不支持泛型所以使用type类型
/// </summary>
/// <param name="abName"></param>
/// <param name="resName"></param>
/// <param name="type"></param>
/// <returns></returns>
//同步加载 根据type指定类型 这里使用System.Type是因为返回的对象Object在Unity和System中均有Object.
public Object LoadResources(string abName, string resName, System.Type type)
{
//加载AB包
AssetBundle ab = LoadAssetBundle(abName);
Object obj = ab.LoadAsset(resName, type);
//Debug.Log("同步加载-指定Type的加载模式");
return obj;
}
#endregion
#region 使用异步加载资源
/// <summary>
/// 异步加载资源-启动协程
/// </summary>
public void LoadResourcesAsync(string abName, string resName, UnityAction<Object> callback)
{
// 开启协程
StartCoroutine(ReallyLoadResourcesAsync(abName, resName, callback));
}
// 异步加载资源的协程
public IEnumerator ReallyLoadResourcesAsync(string abName, string resName, UnityAction<Object> callback)
{
LoadAssetBundleAsync(abName, (tmp)=>
{
AssetBundleRequest abr = tmp.LoadAssetAsync(resName);
if (abr == null || abr.asset == null)
{
Debug.LogError("未能加载资源包");
callback(null);
}
else
{
callback(abr.asset);
}
// 得到资源后abr后返回abr的asset
});
yield return resName;
}
#endregion
#region 使用异步泛型加载资源
/// <summary>
/// 异步加载资源的泛型的方式
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="abName"></param>
/// <param name="resName"></param>
/// <param name="callback"></param>
public void LoadResourcesAsync<T>(string abName, string resName, UnityAction<T> callback) where T : Object
{
// 开启协程
StartCoroutine(ReallyLoadResourcesAsync<T>(abName, resName, callback));
}
// 异步加载资源的协程
public IEnumerator ReallyLoadResourcesAsync<T>(string abName, string resName, UnityAction<T> callback) where T : UnityEngine.Object
{
LoadAssetBundleAsync(abName, (tmp) =>
{
AssetBundleRequest abr = tmp.LoadAssetAsync(resName);
if (abr == null || abr.asset == null)
{
Debug.LogError("未能加载资源包");
callback(null);
}
else
{
callback(abr.asset as T);
}
// 得到资源后abr后返回abr的asset
});
yield return null;
}
#endregion
#region 使用异步Type加载资源
public void LoadResourcesAsync(string abName, string resName, System.Type type, UnityAction<Object> callback)
{
// 开启协程
StartCoroutine(ReallyLoadResourcesAsync(abName, resName, callback));
}
// 异步加载资源的协程
public IEnumerator ReallyLoadResourcesAsync(string abName, string resName, System.Type type, UnityAction<Object> callback)
{
this.LoadAssetBundleAsync(abName, (tmp) =>
{
AssetBundleRequest abr = tmp.LoadAssetAsync(resName, type);
if (abr == null || abr.asset == null)
{
Debug.LogError("未能加载资源包");
callback(null);
}
else
{
callback(abr.asset);
}
// 得到资源后abr后返回abr的asset
});
yield return null;
}
#endregion
#region 卸载AB包
// 单个包的卸载
public void UnloadRes(string abName)
{
if(abDic.ContainsKey(abName))
{
abDic[abName].Unload(false);
abDic.Remove(abName);
}
}
// 所有包的卸载
public void ClearAB()
{
AssetBundle.UnloadAllAssetBundles(false);
abDic.Clear();
mainAB = null;
manifest = null;
}
#endregion
}
5.测试代码
以下是AB包管理器的测试代码, 分为部分小节, 测试的结果是最开始的图片上的效果.
5.1 相关变量和函数
// 同步加载测试
private GameObject cube1;
private GameObject cube2;
private GameObject cube3;
private GameObject floor1;
// 同步加载测试根节点
private GameObject root1;
// 异步加载测试
private GameObject cube4;
private GameObject cube5;
private GameObject cube6;
private GameObject floor2;
// 异步加载测试根节点
private GameObject root2;
// 初始化根节点以便作为区分
private void InitRoot()
{
root1 = new GameObject();
root2 = new GameObject();
root1.name = "同步加载Root";
root2.name = "异步加载Root";
}
5.2 同步加载测试
public void TestLoadResourceAndPackage()
{
// 同步加载测试--Type
cube1 = ABManager.GetInstance().LoadResources("static.ab", "wall", typeof(GameObject)) as GameObject;
cube1 = Instantiate(cube1);
cube1.name = "同步--Type";
cube1.transform.position = Vector3.one * -2;
cube1.transform.SetParent(root1.transform);
// 同步加载测试--泛型
cube2 = ABManager.GetInstance().LoadResources<GameObject>("static.ab", "wall");
cube2 = Instantiate(cube2);
cube2.name = "同步--T泛型";
cube2.transform.position = Vector3.one * -1;
cube2.transform.SetParent(root1.transform);
// 同步加载测试
floor1 = ABManager.GetInstance().LoadResources("static.ab", "floor") as GameObject;
floor1 = Instantiate(floor1);
floor1.name = "同步--Normal";
floor1.transform.position = Vector3.zero;
floor1.transform.SetParent(root1.transform);
}
5.3异步加载测试
public void TestLoadResourceAndPackAsync()
{
// 异步加载测试--只测试包的加载
ABManager.GetInstance().LoadAssetBundleAsync("static.ab", LoadABAsync);
// 异步加载测试
ABManager.GetInstance().LoadResourcesAsync("static.ab", "wall", LoadResAndPackAsyncNormal);
// 异步加载测试--泛型
ABManager.GetInstance().LoadResourcesAsync<GameObject>("static.ab", "wall", LoadResAndPackAsyncT);
// 异步加载测试--Type
ABManager.GetInstance().LoadResourcesAsync("static.ab", "wall", typeof(GameObject), LoadResAndPackAsyncType);
}
// 异步测试AB包
private void LoadABAsync(AssetBundle ab)
{
Debug.Log("测试异步加载只加载资源包的情况, 加载的包名" + ab.name);
}
// 测试异步加载资源-normal
private void LoadResAndPackAsyncNormal(UnityEngine.Object obj)
{
cube4 = Instantiate(obj as GameObject);
cube4.name = "异步--normal";
cube4.transform.position = Vector3.one;
cube4.transform.SetParent(root2.transform);
}
// 测试异步加载资源-T泛型
private void LoadResAndPackAsyncT(GameObject obj)
{
cube5 = Instantiate(obj);
cube5.name = "异步--T泛型";
cube5.transform.position = Vector3.one * 2;
cube5.transform.SetParent(root2.transform);
}
// 测试异步加载资源-Type
private void LoadResAndPackAsyncType(UnityEngine.Object obj)
{
cube6 = Instantiate(obj as GameObject);
cube6.name = "异步--Type";
cube6.transform.position = Vector3.one * 3;
cube6.transform.SetParent(root2.transform);
}
5.4 完整的测试代码
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TestABManager : MonoBehaviour
{
// 同步加载测试
private GameObject cube1;
private GameObject cube2;
private GameObject cube3;
private GameObject floor1;
// 同步加载测试根节点
private GameObject root1;
// 异步加载测试
private GameObject cube4;
private GameObject cube5;
private GameObject cube6;
private GameObject floor2;
// 异步加载测试根节点
private GameObject root2;
void Start()
{
InitRoot();
TestLoadResourceAndpackage();
TestLoadResourceAndPackAsync();
}
private void InitRoot()
{
root1 = new GameObject();
root2 = new GameObject();
root1.name = "同步加载Root";
root2.name = "异步加载Root";
}
// 测试同步加载方式
public void TestLoadResourceAndpackage()
{
// 同步加载测试--Type
cube1 = ABManager.GetInstance().LoadResources("static.ab", "wall", typeof(GameObject)) as GameObject;
cube1 = Instantiate(cube1);
cube1.name = "同步--Type";
cube1.transform.position = Vector3.one * -2;
cube1.transform.SetParent(root1.transform);
// 同步加载测试--泛型
cube2 = ABManager.GetInstance().LoadResources<GameObject>("static.ab", "wall");
cube2 = Instantiate(cube2);
cube2.name = "同步--T泛型";
cube2.transform.position = Vector3.one * -1;
cube2.transform.SetParent(root1.transform);
// 同步加载测试
floor1 = ABManager.GetInstance().LoadResources("static.ab", "floor") as GameObject;
floor1 = Instantiate(floor1);
floor1.name = "同步--Normal";
floor1.transform.position = Vector3.zero;
floor1.transform.SetParent(root1.transform);
}
// 测试异步加载方式
public void TestLoadResourceAndPackAsync()
{
// 异步加载测试--只测试包的加载
ABManager.GetInstance().LoadAssetBundleAsync("static.ab",LoadABAsync);
// 异步加载测试
ABManager.GetInstance().LoadResourcesAsync("static.ab", "wall", LoadResAndPackAsyncNormal);
// 异步加载测试--泛型
ABManager.GetInstance().LoadResourcesAsync<GameObject>("static.ab", "wall", LoadResAndPackAsyncT);
// 异步加载测试--Type
ABManager.GetInstance().LoadResourcesAsync("static.ab", "wall", typeof(GameObject), LoadResAndPackAsyncType);
}
// 异步测试AB包
private void LoadABAsync(AssetBundle ab)
{
Debug.Log("测试异步加载只加载资源包的情况, 加载的包名" + ab.name);
}
// 测试异步加载资源-normal
private void LoadResAndPackAsyncNormal(UnityEngine.Object obj)
{
cube4 = Instantiate(obj as GameObject);
cube4.name = "异步--normal";
cube4.transform.position = Vector3.one;
cube4.transform.SetParent(root2.transform);
}
// 测试异步加载资源-T泛型
private void LoadResAndPackAsyncT(GameObject obj)
{
cube5 = Instantiate(obj);
cube5.name = "异步--T泛型";
cube5.transform.position = Vector3.one * 2;
cube5.transform.SetParent(root2.transform);
}
// 测试异步加载资源-Type
private void LoadResAndPackAsyncType(UnityEngine.Object obj)
{
cube6 = Instantiate(obj as GameObject);
cube6.name = "异步--Type";
cube6.transform.position = Vector3.one * 3;
cube6.transform.SetParent(root2.transform);
}
}