Unity AB包详解

1.AB包压缩的三种方式

在这里插入图片描述
①No Compression:不压缩包,包体最大,但是加载是最快的
②LZMA:包体压缩到最小,但是解压速度最慢,加载的时候会加载整个包
③LZ4:包体压缩中等,速度较快,速度跟No Compression差不多(推荐使用)
下面测试一下三者的加载速度
【图1】
在这里插入图片描述
图1中的model为41.6MB,依照3个选项分别打成3个包,如图2,图3,图4,图5
【图2】
在这里插入图片描述
【图3】
在这里插入图片描述
【图4】
在这里插入图片描述
【图5】
在这里插入图片描述
下面对每个包加载2次,Time单位为毫秒
【图6】
在这里插入图片描述
图6为No Compression,加载速度是最快的

【图7】
在这里插入图片描述
图7为LZMA,加载速度最慢

【图8】
在这里插入图片描述

图8为LZ4,加载速度较快,跟NoCompression速度差不多
注意:AB包只能加载一次,重复加载会报错。要使用Unload卸载加载过的包,如果未加载时调用Unload不会报错

//卸载全部包,bool值为是否清除加载过的资源,true表示清除,但是游戏对象不会被清除,所以会剩下一堆空物体,或者造成材质丢失
AssetBundle.UnloadAllAssetBundles(true);	

AssetBundle bundle = AssetBundle.LoadFromFile(path);
bundle.Unload(true); //卸载单个包,参数同上

2.AB包加载依赖顺序

【图9】
在这里插入图片描述
图9中,models包依赖了texture包,在加载的时候,我们需要优先并且手动加载依赖包,才能使得资源加载正确。
加载依赖包的操作:先加载主包,加载包里的依赖文件,通过依赖文件查询要加载包的依赖关系。

	 if (Input.GetKeyDown(KeyCode.K))
	 {
	     Debug.Log("使用LZ4包");
	     
	     //加载models包,并且实例化包里的unitychan(GameObject类型)
	     AssetBundle b = Load(Application.dataPath + "/../AssetBundles/PCBuild1/models", "unitychan");
	 }
	
	 if(Input.GetKeyDown(KeyCode.J))
	 {
	     Debug.Log("加载依赖包");

		 //加载主包
	     AssetBundle main = Load(Application.dataPath + "/../AssetBundles/PCBuild1/PCBuild1");
	     //加载描述文件
	     AssetBundleManifest manifest = main.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
		 //获取models包的所有依赖关系
	     string[] depend = manifest.GetAllDependencies("models");
	     foreach (string str in depend)
	     {
	     	 //加载models的依赖包
	         Debug.Log("depend==" + str);
	         Load(Application.dataPath + "/../AssetBundles/PCBuild1/" + str);
	     }
	 }

上述代码中,如果先按下J加载依赖包,再按下K加载models包,则模型正常,如图10
【图10】
在这里插入图片描述

如果先加载了models包,后面才加载依赖包,则贴图丢失,如图11
【图11】
在这里插入图片描述

有一点注意的是,我一开始想通过models包直接加载描述文件,获取依赖包,但是发现加载不了,可能只有主包才能获取描述文件

Debug.Log("使用LZ4包");
AssetBundle b = Load(Application.dataPath + "/../AssetBundles/PCBuild1/models", "unitychan");
AssetBundleManifest manifest = b.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
Debug.Log("manifest==" + manifest); //manifest为null

3.AB包资源的自动复制

下面用一个简单的例子解释下依赖包的产生
【图12】
在这里插入图片描述
【图13】
在这里插入图片描述

如图12,图13,Cube有一个blue材质,Sphere有一个red材质。把empty对象打成一个名为empty的包,blue材质打成名为blue_material的包,red材质不打包。通过打包工具就可以看到一些blue和red不一样的地方了
【图14】
在这里插入图片描述
图14,我们发现empty包里有一个red材质,没有blue材质,原因如下:
red材质没有指定包名,所以unity会认为你的red是不需要打依赖包的,自动复制这个资源到empty包中
blue材质手动打了blue_material包,则不会复制资源到empty包中。

【图15】
在这里插入图片描述
如图15,我们只加载empty包,可以看到blue材质丢失了,而red材质是正常加载的,这就说明red已经被自动复制进empty包中了。

4.为什么要打依赖包,什么时候要打依赖包?

还是red材质未设置包名的情况:我们假设不止empty包依赖了材质red,还有其他很多包都依赖了材质red,那么所有依赖red材质的包都会复制一份red材质到他们自己的包里去。这就使得资源浪费,同个资源不应该重复出现在其他地方。
要解决这个办法就是跟我们上面说的一样,将red材质单独打一份包,这样就只会存在一份资源,不会浪费。

结论:依赖包是为了避免资源的浪费;当存在多个包引用同一个资源时,这个资源需要单独打依赖包

5.加载依赖包的正确方式

错误操作:

    private AssetBundle Load(string path, string assetName = "")
    {
        Debug.Log("开始加载资源");
        AssetBundle bundle = AssetBundle.LoadFromFile(path);

        //加载依赖包,这里的manifest已经提前加载了
        string bundleName = path.Substring(path.LastIndexOf('/') + 1);
        string[] depends = manifest.GetAllDependencies(bundleName);
        foreach(string depend in depends)
        {
            Debug.Log("自动加载依赖包==" + depend);
            AssetBundle.LoadFromFile(Application.dataPath + "/../AssetBundles/PCBuild1/" + depend);
        }

        if(!string.IsNullOrEmpty(assetName))
        {
            GameObject instance = bundle.LoadAsset<GameObject>(assetName);
            Instantiate(instance);
        }
        
        return bundle;
    }

这个加载函数会查询要加载的包的依赖,如果并且将他依赖的包都加载一遍(注意这里如果是同时加载的话,那么先加载依赖还是先加载包,都没有问题,不会造成资源引用丢失)。这段代码是有一个问题的,如果有2个包都依赖了同个包,那么这个依赖包会被重复加载
【图16】
在这里插入图片描述
图16中,有empty包和empty2包,这两个包都依赖了blue_material包,所以他加载了2次blue_material包。解决办法就是在加载依赖包前先检查一遍,如下代码。
正确操作:

    private AssetBundle Load(string path, string assetName = "")
    {
        Debug.Log("开始加载资源--" + path);
        AssetBundle bundle = AssetBundle.LoadFromFile(path);

        //加载依赖包,这里的manifest已经提前加载了
        string bundleName = path.Substring(path.LastIndexOf('/') + 1);
        string[] depends = manifest.GetAllDependencies(bundleName);
        foreach(string depend in depends)
        {
            //新增代码
            bool isLoad = false;
            IEnumerable<AssetBundle> bundlesEnumerable = AssetBundle.GetAllLoadedAssetBundles();
            foreach(AssetBundle loaded in bundlesEnumerable)
            {
                if(string.Equals(loaded.name, depend))
                {
                    Debug.Log("已经加载过--" + depend);
                    isLoad = true;
                }
            }

            if(isLoad)
            {
                continue;
            }

            Debug.Log("自动加载依赖包==" + depend);
            AssetBundle.LoadFromFile(Application.dataPath + "/../AssetBundles/PCBuild1/" + depend);
        }

        if(!string.IsNullOrEmpty(assetName))
        {
            GameObject instance = bundle.LoadAsset<GameObject>(assetName);
            Instantiate(instance);
        }
        
        return bundle;
    }

图【17】
在这里插入图片描述
但是这种办法会嵌套遍历,所以更好的办法是用字典把加载过的包名存起来,直接查找字典就行。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值