Unity AssetBundle,Asset,GameObject之间的联系

一.问题

    首先,这里说明一下,我这边的GameObject有点笼统,就是表达的是游戏中的具体实例。html

      

 二.概念

     1)Asset是什么?

          游戏中具体的资源,像texture,mesh,material,shader,script等,实实在在的游戏项目文件夹中所须要堆放的资源。好比,var obj = Resource.Load<GameObject>("Prefabs/testItem"),这个obj就是Asset。程序员

      2)GameObject是什么?

          var gameItem= Instantiate(obj),这个gameItem就是能够存在于游戏的实际场景中(这个比较简单,很少说了)。GameObject是游戏中实际使用的对象(就是你会在屏幕中实际看到的),是由Asset实例化后的对象。本质上其实仍是Asset的衍变,是对部分Asset的引用和复制出来的新东西,其本质仍是Asset。     算法

      3)AssetBundle是什么?

        由上述可知,咱们在游戏中生成实际的物体,须要Asset。而Asset,好比一张图片,也是一个Asset,实际大小1M,游戏中这种图片不少,那就轻轻松松几百M,几个G的Asset,都是颇有可能的。做为程序员,对于这种“原汁原味”拿过来确定不行。好比咱们工做中把文件什么的发给同事的时候都知道压缩一下,能够传输的过程当中小一些。固然了,咱们在游戏开发中使用Asset,也是须要相似的。因而,就推出了AssetBundle这一律念。固然咱们推出AssetBundle,远不止压缩这一需求。可是你须要知道,主要是为了更好的传输,还有好比减小资源大小,利于网络那边的传输,方便加载。网络

        简而言之,AssetBundle就是为了让游戏项目中大量Asset适应实际游戏运行时而被压缩后的一种二进制文件。异步

三.分析

    1)Asset和GameObject的关系?

      ①复制+引用关系测试

      Instaniate一个Prefab(Asset),是对Asset进行clone(复制)+引用结合的过程。GameObject,transform是clone的。其余mesh/texture/material/shader等,这些都是纯引用的。引用的Asset对象不会被复制,只是一个简单的指针指向已经load的Asset对象。专门要提一下Script Asset,Unity里每一个Script都是一个封闭的class定义,并无写调用代码。光class是不会工做的。其实Unity引擎就是那个调用代码。clone一个script asset等于new一个class实例,实例才会工做 ,把它挂到Unity主线程的调用链去,class实例里的update和start才会被执行。多个物体挂同一个脚本,其实就是多个物体挂了挂了那个脚本的类的实例。在new class过程当中,数据区是复制的,代码区是共享的,算是一种复制+引用关系。引用关系的话,会有一对一,一对多,多对一的关系。以下图:优化

      

      ②释放ui

        若是你Destroy(GameObject,这个是游戏中具备实例),只是释放了clone的asset,引用的asset并不会被干掉。还有由于destroy并不知道有没有被别的asset引用。可是若是你想把asset也释放,有两种方案。加密

        I. Resources.UnloadUnusedAssets()spa

          释放当前全部的没有被引用的(无用)asset,可是不能保证释放掉当前的被引用的资源(由于可能还被其余资源引用,就不会去释放了)。缺点:异步,会卡。因为Unity资源的相互引用关系比较复杂,想要明确判断某一资源不存在引用关系是有必定难度的,而且,若是我 们想要释放的资源存在隐形的引用关系,UnloadUnUsedAssets将会无视这个资源而无任何反馈。根据实战来看,最佳使用的时机是在场景切换进入新的场景后,Unity场景关闭会有效的销毁全部的对象和全部代码的引用,即在新场景开头最为稳妥。必要时加上GC.Collect().

        II.Resouce.UnLoad(obj)     

          释放当前实例的全部被引用的asset(无论这个asset是否还有被引用,因此,风险很大,除非保证肯定被他引用的资源没有再被其余资源引用,通常用于单独的一张纹理释放)缺点:风险太大,容易被报:UnloadAsset may only be used on individual assets and can not be used on GameObject's / Components or AssetBundles,不能用做卸载GameObject,只能用于纹理释放。否则会报这个错,我也不知道为啥,有知道的告我一下,额,还有就是这个方法不多用,应用的话,也只是对纹理释放。

          对于这种使用,对于资源大的且无引用的可使用,若是资源消耗不大,能够等到场景切换,使用I中 Resources.unloadUnusedAssets方案。

        III.GameObject.DestroyImmediate(asset,true)

          代替上述方案,能够针对卸载asset(好像还能够直接使用GameObject.Destroy(asset) ,也能够直接使用。(待验证))
        IV.AssetBundle.unload(true)  !!!慎用

          这个也能够卸载asset,可是卸载的是这个assetBundle里面的全部的asset。(后面详说)

    2)AssetBundle和Asset的关系

         ①包含,依赖关系

            一个AssetBundle中能够包含一个或多个Asset。一个Asset依赖于AssetBundle。

         ②释放

          AssetBundle的释放只能经过如下两种方式释放。即便系统在加载新场景的时候全部的内存对象都会被自动销毁,包括你用AssetBundle.load加载的对象和Instaniate克隆的,可是不包括AssetBundle文件自己的内存镜像,那个必需要用Unload来释放,用.Net术语说该资源是非托管的。

            I.AssetBundle.Unload(false) 使用频率较多

              用AssetBundle.Load加载须要的asset以后应该当即使用unlaod(false),释放assetbundle文件自己的内存镜像,但不销毁该assetBundle加载过的asset对象。(尽可能释放一部份内存,大多数游戏这么作)      

            II.AssetBundle.Unload(true)

              释放该assetBundle文件镜像并释放该assetBundle全部loaded的asset内存对象。(风险很大,由于通常不太能肯定是否该loaded的asset是否还被其余资源引用)。           

四.实例测试

     AssetBundle和Asset 项目工程中大小分析

      ①首先,准备10个同样大小的texture(为了测试结果更加准备,每一个图1.33M)

        

 

          texture 的原大小,即Asset自己的大小:1.33M *10 = 13.3M

      ②LZ4和LZMA打包方式案例对比      

        I.BuildAssetBundleOptions.None 打包方式,该为默认压缩,即LZMA,在使用AssetBundle以前须要解压缩。使用LAMA格式压缩的AssetBundle的包体最小

(高压缩比),可是会增长相应的解压缩时间和内存。

BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.None,BuildTarget.StandaloneWindows64);

        以上述方式打包完以后 每张   texture :613K *10 = 6M

执行下面代码。(为方便数据对比,是在找不到大的texture了)

        

       使用AssetBundle.LoadFromFile 加载那10个AssetBundle以后的内存显示

        

        对比上2张图的消耗,大概消耗了13.6M的Unity内存,0.5M其余内存。由于是十张纹理,原纹理没有打包以前的大小大概是1.33m,也就是说,大概技术释放的是原Asset的大小。

 

        再Load其中的Asset

        

        由上图可知,申请的是Unity内存爆增100M左右,Mono的内存也相应的增长了。

        II.ChunkBasedCompression,即LZ4压缩方式,压缩比通常,压缩后的包体较大,可是解压速度快,消耗内存小。

BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.ChunkBasedCompression,BuildTarget.StandaloneWindows64);

        以上述方式打包完以后 每张texture :1110K*10=11100K,11M左右 

         再执行同上一致的Load脚本.   

      AssetBundle,Asset,GameObject 加载中的内存对比

        

      同上述步骤一致,给出LoadAssetBundle,即AssetBundle.LoadFromFile后的内存大小

         

     大概消耗2M内存。(上述LAMA方式打出的包是23.5M,相差10倍啊,果断使用这种方式,咱们项目也是这种方式)  

      再给出LoadAsset以后的内存图

      

       LoadAsset以后,Unity内存,Mono内存都增长不少

        III.总结对比,I加载的时候LoadAssetBundle消耗明显大于II方式10倍(甚至不止,由于总量越大,差距就越大)。移动游戏中的内存多么珍贵你们懂得。使用ChunkBasedCompression,即LZ4压缩,更为划算。由于宁愿牺牲一点包体大小,也要消耗内存小一些。(咱们游戏项目是这个需求,具体仍是看项目吧)

固然还有其余打包选项,各有利弊,具体看游戏实际状况需求。我这里只是对比出了告诉你打包选项会决定你的打出的资源包体大小和游戏中加载消耗内存。很重要。

具体打包选项BuildAssetBundleOptions参考:https://blog.csdn.net/AnYuanLzh/article/details/81485762

      补充:由上,咱们知道打包时压缩方式会致使打出的包体和加载时的消耗都不一样。加载时候的加载方式咱们这边使用AssetBundle.CreateFromFile直接加载AssetBundle,Unity其实还提供了WWW加载AssetBundle的方式。可是这里不做详述了(讲不完...)。

五.总结    

      由上可知,文章虽然说AB,Asset,GameObject三者联系,可是GameObject主要是由Asset实例化而来,GameObject是Asset的引用和复制的关系(主要引用),这个也能够说是Asset的一种。问题也就能够简化为AB和Asset之间的关系。

  

     由上图,再总结一下,打包方式不一样,加载方式不一样,形成的消耗不一样。即,若是想优化游戏中的资源,须要注意打包AB的方式,以及加载AB的方式。固然,还要注意AB的卸载,和Asset的卸载。

六.参考    

    关于加载AssetBundle和加载Asset的区别,详情就很少说了,见UWA:https://blog.uwa4d.com/archives/ABTheory.html

    打包加密压缩算法区别参考:https://www.cnblogs.com/murongxiaopifu/p/5629415.html#autoid-3-3-0     

    附:测试Demo源码:连接:https://pan.baidu.com/s/1ZHPoQbuxgdUVh9PGbz6ArA   提取码:7lkv 

相关文章
相关标签/搜索