目录

一、AssetBundles-Browser-master的安装

二、AssetBundles-Browser界面的介绍

三、关于AB包的一些常用API的介绍

1、加载AB包的API(1)AssetBundle.LoadFromFile()这个是同步加载 这样就是加载好了AB包

2、加载AB包的API(2)AssetBundle.LoadFromFileAsync()这个是异步加载 请注意他们之间的返回值不同哦

3、同步加载AB包中的资源(1)加载好的AB包.LoadAsset<资源类型>("资源名") 这是通过泛型的方式进行加载资源

4、同步加载AB包中的资源(2)加载好的AB包.LoadAsset("资源名", typeof(资源类型)) as 资源类型

5、异步加载AB包中的资源 加载好的AB包.LoadAssetAsync<资源类型>(资源名)这个是需要加载好对应的AB包之后才能使用的哦

加载讲完了,现在来讲讲资源的卸载,资源的卸载一般分为两种,卸载全部AB包,卸载指定AB包,分别对应两种API AssetBundle.UnloadAllAssetBundles(false); ab.Unload(false)

四、AB包之间的依赖关系

五、ABMgr的实现


      AssetBundle是用于热更新使用的一种资源格式文件,那什么是热更新呢?游戏或者软件更新时。无需重新下载客户端进行安装,而是在应用程序启动的情况下,在内部进行资源或者代码的更新。说人话,就是不用将游戏重新打整包出去,而是很快捷的即时更新,这就是热更新。而AssetBundle就是特定于平台的资产压缩包,有点类似于压缩文件,资产包括:模型,贴图,预设体,音效,材质球等等。但是不能打包代码。这里的特定平台有很多,例如Android iOS PC 对应不同的平台,我们需要打不同的AB包来使用。

        AB包主要是用于减少初始包体体积的,我们将打包好的资源文件放在远端服务器,然后远程下载更新就行。

------------------------------------------------------工具安装---------------------------------------------------------                

一、AssetBundles-Browser-master的安装

       将本文提供的这个工具解压之后,直接将文件夹拖入即可。如果有报错的话,将Test相关的文件直接删除即可。因为这个是比较老的工具,不太适应新版本的Unity。导入完毕之后,你可以看到如下图片:这个资源当时手滑设置成了VIP资源 可以去github找一找 或者 使用 Package Manager。打开 Window > Package Manager,点击左上角的 + 号,选择 “Add package from git URL…”,输入官方包名 com.unity.assetbundlebrowser,Unity 会自动以包的形式引入。

到这里基本上就是装好了,我们可以打开看看里面是什么

可以看到有三个小界面,我们后面再一一介绍。

--------------------------------------------------界面介绍---------------------------------------------------------

二、AssetBundles-Browser界面的介绍

                                                                图Configure界面

这个界面就是显示你目前已经打包好的AB包,这里我自己随便打了两个。那么怎么打包AB包呢,很简单,往下看

看!我们选中了一个资产文件,同时注意我框选的地方,我们只需要将右下角的这个AssetBundle设置好就行了了。

点击这个new就可以新建AB包资源文件了 我这里新建了一个test包,注意这个是默认首字母给你改成小写的

会到这个界面,我们发现已经设置好了一个test包,但是这个时候并没有AB包的产生,因为我们还没有打包,需要到Build中才能进行打一个AB包

来到Bulid界面中,下面标注的都是比较重要的内容,重点关注即可

关于上图的一些小内容的补充:

Clear Folders 就是打包的时候是否清空文件夹,清空需要花费的时间比较长,会清空不存咋包

关于压缩格式:

NoCompression 就是不压缩 解压快 但是包比较大 不推荐

LZMA 压缩最狠,但是解压慢,缺点是 用包中的一个资源,需要解压所有的包

LZ4 压缩之后的包 相比较LZMA会大一点 但是用什么就解压什么包 内存的占用较低

关于下面的一个小选择:不常用默认即可,这里用缩写表示

ETI:在资源包中 不包含资源的类型信息 一般不选 类型信息有时候挺有用的

FR:    重新打包时候需要重新构建包,和ClearFoders不同,他不会删除不再存在的包,

ITTC: 增量构建检查时,忽略类型数的更改 没啥用 就是你只更改了资源的类型 但是并没有实际增加或减少 就不会重新构建包 减少了打包时间

Apped Hash 将文件哈希值附加到资源包名上 就像是给他一个身份证

SM 严格模式 如果打包的时候出错了 那么打包无效 无法成功

DRB 此选项允许您针对 AssetBundle 进行干运行构建,但不实际构建它们。(看不懂)

打包之后呢,你会有这样几个变化

1、你会发现你的Asset文件夹下多了个这个东西,这个PC叫做主包,test呢就是刚才打的AB包了

2、当我们打开项目工程时候 会发现这里也多了一个文件夹

是的 到这里 你就已经成功的打了一个AB包了!很棒!继续加油。

-----------------------------------------------AB包的API介绍------------------------------------------------------------

三、关于AB包的一些常用API的介绍

        加载AB包中的资源一般分为两步,1、加载该资源所属的AB包 2、从该AB包中加载资源

1、加载AB包的API(1)AssetBundle.LoadFromFile()这个是同步加载 这样就是加载好了AB包

//               这是一个静态方法               这里是你的AB包路径
AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "test");

2、加载AB包的API(2)AssetBundle.LoadFromFileAsync()这个是异步加载 请注意他们之间的返回值不同哦

AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath + "/" + ABName);

上述的两种方式就是加载AB包的方法,现在来介绍加载AB包中的资源的方法

3、同步加载AB包中的资源(1)加载好的AB包.LoadAsset<资源类型>("资源名") 这是通过泛型的方式进行加载资源

GameObject obj1 = ab.LoadAsset<GameObject>("Plane");

4、同步加载AB包中的资源(2)加载好的AB包.LoadAsset("资源名", typeof(资源类型)) as 资源类型

GameObject obj2 = ab.LoadAsset("Plane", typeof(GameObject)) as GameObject;

5、异步加载AB包中的资源 加载好的AB包.LoadAssetAsync<资源类型>(资源名)这个是需要加载好对应的AB包之后才能使用的哦

AssetBundleRequest abq = abcr.assetBundle.LoadAssetAsync<GameObject>(resName);

注意哦 异步加载一般是结合着协程一起使用的,或者回调函数,因为你需要等他加载完毕时候才能正常使用的。

例如:

    IEnumerator LoadResAsync(string ABName, string resName) {
        AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath + "/" + ABName);
        yield return abcr;
        AssetBundleRequest abq = abcr.assetBundle.LoadAssetAsync<GameObject>(resName);
        yield return abq;
        // 到这里就加载完毕了
    }

加载讲完了,现在来讲讲资源的卸载,资源的卸载一般分为两种,卸载全部AB包,卸载指定AB包,分别对应两种API AssetBundle.UnloadAllAssetBundles(false); ab.Unload(false)

// 资源卸载
// 单个卸载
ab.Unload(false); // 和下面的true false同意思
// 卸载所有加载的AB包参数为ture会把通过AB包加载的资源也卸载了 就是场景上的对象也会直接丢失
AssetBundle.UnloadAllAssetBundles(false);

-------------------------------------------------------依赖关系的介绍----------------------------------------------------

四、AB包之间的依赖关系

        假如你有一个资源在A包中,该资源的材质在B包中,那如果你只加载A包会发生什么呢?很显然,你会发生材质丢失的问题,那该怎么办呢?也很简单,加载B包就完了,但是,我如果有很多很多包之间都存在依赖关系呢?你会说,毁灭吧 这谁整出来的互相文件乱打包啊。不过没事的,有解决方法的!记得前面提到的主包吗,明明我们只需要打包好的资源包,但却多了一个主包,这是为啥呢,这其实就是用来解决依赖关系的,主包里面包含了所有包与包之间的依赖关系,所以说啊 山重水复疑无路 这不就有解决方法了吗,遇到困难的时候 解决困难才能一劳永逸!下面来看看吧。

         主包也是AB包,所以我们可以采用加载AB包的方式加载主包如下:

AssetBundle abMain = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "PC");

 这里就是获取依赖关系的关键写法了!固定的 直接这样写就行 获取的这个变量就是依赖关系文件 我们只要合理使用就行

AssetBundleManifest abMainManifest = abMain.LoadAsset<AssetBundleManifest>("AssetBundleManifest");

这样子使用 传入的参数就是你想要获取的某个包的包名 这样就会给你返回一个字符串数组 这个数组就是需要依赖的包名

// 从固定文件中 得到依赖关系
string[] strings = abMainManifest.GetAllDependencies("model");
for (int i = 0; i < strings.Length; i++) {
    Debug.Log(strings[i]);// 这里打印的就是依赖的AB包名了
    AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + strings[i]);
}

到此 我们的AB包就算学习的差不多了,努力哦。下一篇文章,我会写一个关于AB包的单例管理器,好的 今天的文章到此结束!

-----------------------------------------将将继续更新了---------------------------------------------------------------

-------------------------------------------------ABMgr单例管理器的实现--------------------------------------------

五、ABMgr的实现

接下来我们会实现一个ABMgr,是一个单例模式实现的AB包的全局管理器,对外可以提供同步加载资源和异步加载资源,同时还可以卸载指定的AB包,或者卸载不需要的AB包

         主要的实现思路就是,用字典记录所有已经加载过的AB包,对了上面忘记说明了,同一个AB包只能加载一次,如果多次加载就会报错的。

        同样,加载一个AB包中的资源需要先加载对应的AB包才能获取到,同时有可能不同的包之间存在依赖,所以我们必须先加载所有依赖包然后再加载我们的目标包。下面的代码在异步加载资源的时候并没有异步处理加载AB包,因为这里会有一个时序问题,如果你短时间同时调用一个异步加载AB包的话,但是AB包并没有加载出来,你这样就会导致重复加载,就会报错,如果你想实现异步加载AB包的话,千万千万注意该问题

        代码如下:

using System.Collections;
using System.Collections.Generic;
using System.Xml.Linq;
using UnityEngine;
using UnityEngine.Events;

public class ABMgr : SingletonAutoMono<ABMgr> {
    /// <summary>
    /// 这个是包的前缀路径
    /// </summary>
    private string PathUrl {
        get {
            return Application.streamingAssetsPath + "/";
        }
    }
    /// <summary>
    /// 这个是不同平台下的主包名
    /// 使用来找依赖关系的
    /// </summary>
    private string MainABname {
        get {
#if UNITY_IOS
            return "IOS";
#elif UNITY_ANDROID
            return "Android";
#else
            return "PC";
#endif
        }
    }
    // 记录所有加载过的AB包 因为AB包不能重复加载
    private Dictionary<string, AssetBundle> abDic = new Dictionary<string, AssetBundle>();
    // 主包名
    private AssetBundle abMain = null;
    // 依赖
    private AssetBundleManifest abMainManifest = null;
    // 临时的AB包
    private AssetBundle ab = null;


    public void LoadAB(string aBName) {
        // 先加载主包
        // 然后找出依赖关系
        if (abMain == null) {
            abMain = AssetBundle.LoadFromFile(PathUrl + MainABname);
            abMainManifest = abMain.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        }
        // 加载需要依赖的包
        string[] strs = abMainManifest.GetAllDependencies(aBName);
        for (int i = 0; i < strs.Length; i++) {
            if (!abDic.ContainsKey(strs[i])) {
                ab = AssetBundle.LoadFromFile(PathUrl + strs[i]);
                abDic.Add(strs[i], ab);
            }
        }
        // 加载完对应的依赖关系后
        // 加载当前的包
        if (!abDic.ContainsKey(aBName)) {
            ab = AssetBundle.LoadFromFile(PathUrl + aBName);
            abDic.Add(aBName, ab);
        }
    }

    /// <summary>
    /// 同步加载某个资源 未优化版 
    /// </summary>
    /// <param name="aBName">ab包名</param>
    /// <param name="resName">资源名</param>
    /// <returns></returns>
    public Object LoadRes(string aBName, string resName) {
        // AB包
        LoadAB(aBName);
        Object obj = abDic[aBName].LoadAsset(resName);
        if (obj is GameObject) {
            return Instantiate(obj);
        } else {
            return obj;
        }
        // 最后加载对应的资源
    }
    /// <summary>
    /// 同步加载根据泛型加载资源
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="aBName"></param>
    /// <param name="resName"></param>
    /// <returns></returns>
    public T LoadRes<T>(string aBName, string resName) where T : Object {
        // AB包
        LoadAB(aBName);
        T obj = abDic[aBName].LoadAsset<T>(resName);
        if (obj is GameObject) {
            return Instantiate(obj);
        } else {
            return obj;
        }
        // 最后加载对应的资源
    }
    /// <summary>
    /// 同步加载 指定资源类型
    /// </summary>
    /// <param name="aBName"></param>
    /// <param name="resName"></param>
    /// <param name="type"></param>
    /// <returns></returns>
    public Object LoadRes(string aBName, string resName, System.Type type) {
        // AB包
        LoadAB(aBName);
        Object obj = abDic[aBName].LoadAsset(resName, type);
        if (obj is GameObject) {
            return Instantiate(obj);
        } else {
            return obj;
        }
    }

    /// <summary>
    /// 异步加载资源 根据名字异步加载
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <param name="callback"></param>
    public void LoadResAsync(string abName, string resName, UnityAction<Object> callback) {
        StartCoroutine(ReallyLoadResAsync(abName, resName, callback));
    }
    private IEnumerator ReallyLoadResAsync(string abName, string resName, UnityAction<Object> callback) {
        LoadAB(abName);
        AssetBundleRequest abr = abDic[abName].LoadAssetAsync(resName);
        yield return abr;
        if (abr.asset is GameObject) {
            callback(Instantiate(abr.asset));
        } else {
            callback(abr.asset);
        }
    }
    /// <summary>
    /// 根据类型加载
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <param name="type"></param>
    /// <param name="callback"></param>
    public void LoadResAsync(string abName, string resName, System.Type type, UnityAction<Object> callback) {
        StartCoroutine(ReallyLoadResAsync(abName, resName, type, callback));
    }
    private IEnumerator ReallyLoadResAsync(string abName, string resName, System.Type type, UnityAction<Object> callback) {
        LoadAB(abName);
        AssetBundleRequest abr = abDic[abName].LoadAssetAsync(resName, type);
        yield return abr;
        if (abr.asset is GameObject) {
            callback(Instantiate(abr.asset));
        } else {
            callback(abr.asset);
        }
    }

    /// <summary>
    /// 根据泛型异步加载
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <param name="callback"></param>
    public void LoadResAsync<T>(string abName, string resName, UnityAction<T> callback) where T : Object {
        StartCoroutine(ReallyLoadResAsyncc<T>(abName, resName, callback));
    }
    private IEnumerator ReallyLoadResAsyncc<T>(string abName, string resName, UnityAction<T> callback) where T : Object {
        LoadAB(abName);
        AssetBundleRequest abr = abDic[abName].LoadAssetAsync<T>(resName);
        yield return abr;
        if (abr.asset is GameObject) {
            callback(Instantiate(abr.asset) as T);
        } else {
            callback(abr.asset as T);
        }
    }
    /// <summary>
    /// 卸载指定AB包
    /// </summary>
    /// <param name="abName"></param>
    public void UnLoad(string abName) {
        if (abDic.ContainsKey(abName)) {
            abDic[abName].Unload(false);
            abDic.Remove(abName);
        }
    }
    /// <summary>
    /// 清理所有AB包
    /// </summary>
    public void Clear() {
        abDic.Clear();
        AssetBundle.UnloadAllAssetBundles(false);
        abMain = null;
        abMainManifest = null;
    }
}

Logo

这里是一个专注于游戏开发的社区,我们致力于为广大游戏爱好者提供一个良好的学习和交流平台。我们的专区包含了各大流行引擎的技术博文,涵盖了从入门到进阶的各个阶段,无论你是初学者还是资深开发者,都能在这里找到适合自己的内容。除此之外,我们还会不定期举办游戏开发相关的活动,让大家更好地交流互动。加入我们,一起探索游戏开发的奥秘吧!

更多推荐