Unity--AssetBundle AB包管理器

1.简介

AB包(AssetBundle)是Unity中用于资源管理的一种机制,它允许开发者将多个文件(如纹理、模型、音频等)打包成一个单独的文件,以便于在游戏运行时动态加载和卸载。 但是现在出现了最新的Addressable来代替AssetBundle.

Addressables是Unity提供的一个资源管理系统,它是对AssetBundle系统的一种改进和扩展。Addressables允许开发者通过地址(名称或标签)来请求资源,而不是直接操作AssetBundle文件。这使得资源的管理和加载变得更加简单和灵活。现在目前只接收AssetBundle的用法个管理器的代码

2.特点

  1. 资源优化:通过将相关资源打包在一起,可以减少文件数量,简化资源管理。

  2. 减少启动时间:游戏开始时不需要加载所有资源,只需加载必要的AB包,从而减少启动时间。

  3. 动态内容更新:可以单独更新AB包中的内容,而不需要重新下载整个游戏。

3.工作流程

AB包的基本工作流程包括以下几个步骤:

  1. 构建AB包:在Unity编辑器中,将需要打包的资源分配到相应的AB包中,并设置好它们的名称和变体。
  2. 打包:使用Unity的构建系统将资源打包成AB包文件。这些文件通常具有.unity3d扩展名。我们类自己命名都是可以的
  3. 加载AB包:在游戏运行时,通过代码动态加载所需的AB包。可以使用AssetBundle.LoadFromFileAssetBundle.LoadFromMemory等方法。
  4. 使用资源:从AB包中加载具体的资源,如纹理、预制体等。
  5. 卸载AB包:当AB包中的资源不再需要时,应该卸载AB包以释放内存。

在使用AB包时,需要注意资源依赖关系的管理,确保在加载资源时,其依赖的所有资源都已正确加载。此外,AB包的版本控制和更新策略也是开发过程中需要考虑的重要方面。

4.AB包管理器

为了管理ab包, 在需要的时候加载ab包, 因此,我们需要建立一个AB包的管理器, 该管理器提供了加载AB包的方式, 为了方便使用者, 可以通过该管理器直接快速加载某AB包里的某个资源.

4.1 效果图和部分说明

以下是AB包加载效果
Unity-AB包-AB包管理器效果图.jpg

通过AssetBundleBrowerk可以查看到打的ab包
Unity-AB包-AB包打包图.jpg

说明:

  • 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存放预制体. 依赖关系如如下图. 下图中的右图是简化版本. 左图中显示了包与包的依赖关系, 右图展示了贴图-材质-预制体(模型)的关系.

Unity-AB包-依赖关系图.png

根据AssestBundle的加载资源包(ab包)的规则, 如果你想要加载一个资源包,需要先加载该资源包的依赖包,

如果不先加载依赖包,那么运行的效果可能就是丢失了材质/贴图/脚本或者其他资源导致程序出现异常或者崩溃.

因此, 理清楚依赖关系非常重要. 依赖关系可以在主包中的mainfest文件中获得. 依赖关系就是上图中的线条

所以, 加载顺序为: 加载主包->读取依赖关系->加载依赖包->加载目标包.

在一些很大的游戏中,资源包很大, 因此如果加载资源包等待的时间很长这会导致玩家失去耐心, 和何况在这个短平快的时代. 因此可以根据资源包的大小来进行同步加载异步加载. 同理, 加载完AB包后, 里面的资源可能很大,可能很小, 这需要根据实际来同步加载资源异步加载资源.

以下代码是AB包管理器, 用于加载AB包, 其中也包含了加载资源的同步方式和异步方式. 下图是ABManager.cs的代码结构.

Unity-AB包-AB包管理器代码结构.jpg

以下是具体代码:

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_IOSreturn "IOS";
#elif UNITY_ANDROIDreturn "Android"
#elsereturn "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_IOSreturn "IOS";
#elif UNITY_ANDROIDreturn "Android"
#elsereturn "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()
{// 同步加载测试--Typecube1 = 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);// 异步加载测试--TypeABManager.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(){// 同步加载测试--Typecube1 = 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);// 异步加载测试--TypeABManager.GetInstance().LoadResourcesAsync("static.ab", "wall", typeof(GameObject), LoadResAndPackAsyncType);}// 异步测试AB包private void LoadABAsync(AssetBundle ab){Debug.Log("测试异步加载只加载资源包的情况, 加载的包名" + ab.name);}// 测试异步加载资源-normalprivate 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);}// 测试异步加载资源-Typeprivate void LoadResAndPackAsyncType(UnityEngine.Object obj){cube6 = Instantiate(obj as GameObject);cube6.name = "异步--Type";cube6.transform.position = Vector3.one * 3;cube6.transform.SetParent(root2.transform);}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/404199.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

docker部署drawio

1&#xff09;介绍Drawio.io GitHub&#xff1a;GitHub - jgraph/drawio: draw.io is a JavaScript, client-side editor for general diagramming. Draw.io是一款开源的绘制流程图的工具&#xff0c;拥有大量免费素材和模板。程序本身支持中文在内的多国语言&#xff0c;创建…

【学习笔记】多元线性回归模型 —— Matlab

文章目录 前言一、多元线性回归多元线性回归模型线性模型 ( Y , X β , σ 2 I n ) (Y,X\beta,\sigma^2I_n) (Y,Xβ,σ2In​) 考虑的主要问题多元线性回归模型的参数估计多元线性回归模型和回归系数的检验 二、示例三、代码实现----Matlab1.多元回归的实现2.逐步回归的实现3.M…

图像增强技术简介

目录 一、概论 二、图像噪声 三、图像增强处理分类 一、概论 图像增强作为基本的图像处理技术&#xff0c;其目的是对图像进行加工&#xff0c;以得到对具体应用来说视觉效果更“好”更“有用”的图像。图像增强算法并不能增加原始图像的信息&#xff0c;而是通过某种技术手…

MVCC 详解

MVCC 简单理解 MVCC&#xff0c;全称 Multi-Version Concurrency Control&#xff0c;是多版本并发控制的意思。 在高并发情况下操作数据库可能会出现脏写、脏读、不可重复度、幻读这四个问题。通过 MVCC 可以实现在不加锁的前提下避免一些问题。 MVCC 的实现原理 多版本 …

相似度计算方法-编辑距离 (Edit Distance)

定义 编辑距离&#xff08;Edit Distance&#xff09;&#xff0c;也称为Levenshtein距离&#xff0c;是一种衡量两个字符串相似度的方法。它定义为从一个字符串转换为另一个字符串所需的最少单字符编辑操作次数&#xff0c;这些操作包括插入、删除或替换一个字符。 计算方法 …

Openstack二层网络的构建和使用

Openstack二层网络的构建和使用 一、实验目的 &#xff08;1&#xff09;了解网络层级、子网、动态地址、网关代理等概念并进行应用。 &#xff08;2&#xff09;了解OpenStack项目以及相关组件。 &#xff08;3&#xff09;了解 Neutron 二层网络的构建和使用。 二、实验原…

tomcat Listener 内存马浅谈

本文来源无问社区&#xff0c;更多实战内容可前往查看http://www.wwlib.cn/index.php/artread/artid/3651.html Tomcat 介绍 Tomcat的主要功能 toncat作为一个web服务器&#xff0c;实现了两个核心的功能 http 服务器功能&#xff1a;进行socket 通信&#xff08;基于TCP/I…

案例分享—国外深色UI界面设计赏析

在国外&#xff0c;深色界面设计&#xff08;Dark Mode&#xff09;已成为提升用户体验的重要趋势。它不仅有效减少屏幕亮度&#xff0c;保护用户视力&#xff0c;还能在夜晚或低光环境下提供更加舒适的浏览体验。设计师们普遍认识到&#xff0c;深色主题不仅提升了应用的视觉层…

Vue中下载内容为word文档

1.使用 html-docx-js&#xff1a;这是一个将 HTML 转换为 Word 文档的库。 2. 利用 Blob 和 FileSaver.js&#xff1a;创建并下载生成的 Word 文档。 在 Vue.js 中实现步骤如下: 1. npm 安装 html-docx-js 和 file-saver npm install html-docx-js npm install file-saver2.…

【vue教程】六. Vue 的状态管理

目录 往期列表本章涵盖知识点回顾Vuex 的基本概念什么是 Vuex&#xff1f;为什么需要 Vuex&#xff1f; Vuex 的核心概念stategettersmutationsactionsmodules Vuex 的安装和基本使用安装 Vuex创建 store在 Vue 应用中使用 store在组件中访问和修改状态 Vuex 的模块化模块化的好…

2024新型数字政府综合解决方案(七)

新型数字政府综合解决方案通过集成人工智能、大数据、区块链和云计算技术&#xff0c;创建了一个高度智能化和互联互通的政府服务平台&#xff0c;旨在全面提升行政效率、服务质量和透明度。该平台实现了跨部门的数据整合与实时共享&#xff0c;利用人工智能进行智能决策支持和…

PCRNet: Point Cloud Registration Network using PointNet Encoding 论文解读

目录 一、导言 二、先导知识 1、Frobenius范数 三、相关工作 1、点云配准工作 2、PointNet 3、基于深度学习的点云配准 四、PCRNet 1、PCRNet 2、Iterative PCRNet 3、损失函数 五、实验 一、导言 本论文收录于CVPR2019&#xff0c;本论文提出了一种依赖PointNet网…

11.2.0.4 RAC 节点1重做操作系统后如何复原

环境描述&#xff1a;Redhat7.9 11.2.0.4 RAC 双节点 实验背景 群里有大佬在交流RAC中1个节点操作系统坏了如何修复&#xff0c;故有了该实验。 在正常的生产环境当中&#xff0c;有时候会遇到主机磁盘以及其他硬件故障导致主机OS系统无法启动&#xff0c;或者OS系统本身故障…

【海奇HC-RTOS平台E100-问题点】

海奇HC-RTOS平台E100-问题点 ■ btn 没有添加到group中 &#xff0c;怎么实现的事件的■ 屏幕是1280*720, UI是1024*600,是否修改UI■ hc15xx-db-e100-v10-hcdemo.dtb 找不到■ 触摸屏驱动 能否给个实例■ 按键驱动■ __initcall(projector_auto_start)■ source insigt4.0 #if…

【esp32程序编译提示undefined reference to ‘xxxx‘】

案例1&#xff1a; 【背景】 在使用SquareLine Studio设计UI时&#xff0c;成功导出UI代码&#xff0c;在编译代码的时候提示undefined reference to ‘ui_img_1869164015’&#xff0c;有一个变量无法识别&#xff0c;没有定义。 【定位步骤】 1.首先找到用这个变量的.c文件…

复现DOM型XSS攻击(1-8关)

目录 第一关&#xff1a;​ 分析代码&#xff1a; 第二关&#xff1a; 分析代码&#xff1a; 第三关&#xff1a; 分析代码&#xff1a; 第四关&#xff1a; 分析代码&#xff1a; 第五关&#xff1a; 分析代码&#xff1a; 第六关&#xff1a; 分析代码&#xff1…

SpringBoot依赖之Spring Data Redis 一 List 类型

概念 Spring Data Redis (AccessDriver) 依赖名称: Spring Data Redis (AccessDriver)功能描述: Advanced and thread-safe Java Redis client for synchronous, asynchronous, and reactive usage. Supports Cluster, Sentinel, Pipelining, Auto-Reconnect, Codecs and muc…

Spring源码-源码层面讲解bean标签添加了lookup-method和replaced-method标签之后源码执行流程,以及对象实例化的流程

bean.xml文件添加lookup-method和replaced-method标签 <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:sch…

C语言部分内存函数详解

C语言部分内存函数详解 前言1.memcpy1.1基本用法1.2注意事项**目标空间与原空间不能重叠****目标空间原数据会被覆盖****目标空间要够大****拷贝字节数需小于原空间大小** 1.3模拟实现 2.memmove2.1基本用法2.2注意事项2.3模拟实现 3.memset3.1基本用法 4.memcmp4.1基本用法4.2…

[论文笔记]ZeRO: Memory Optimizations Toward Training Trillion Parameter Models

引言 今天带来ZeRO: Memory Optimizations Toward Training Trillion Parameter Models的论文笔记。 大型深度模型提供了显著的准确性提升&#xff0c;但训练数十亿到数万亿个参数是具有挑战性的。现有的解决方案&#xff0c;如数据并行和模型并行&#xff0c;存在基本的局限…