在上段时间摸索了Unity5的assetbundle用法之后,我在项目里面全面的使用起来,于是发现了一些坑,这里和大家分享一下,顺便说说我是怎样解决的。
首先是图集打包的问题。这个问题在unity5.2版本已经解决了,但在5.2之前确实是一个bug来的。具体的表现是,你把同一个图集标签的散图打包成同一个assetbundle时,这个assetbundle既包含了图集的大图,也包含了散图本身,变成了重复打包,容量暴增。这个问题困扰了我一小段时间,后来尝试着下载了个unity5.2,就没有这个问题了,图集散图打包成一个assetbundle,只会保留图集的大图作为texture2d,那些散图全部都只有sprite,没有texture2d了。
第二个问题,其实也是图集引起的。具体表现是如果你的UI(指的是UGUI)上面使用了图集,然后把UI的预设打包Assetbundle,那么就算UI依赖的图集本身已经设置了AssetName,但UI的Assetbundle本身还是会包含图集的大图来打包,又重复打包了,容量同样暴增。这个问题在5.2依然存在。
第三个问题,Unity5的Assetbundle是自动寻找需要重新打包的内容来增量打包的,这个判断有时候会不准确。具体表现是,比如刚才那些重复打包的内容,我在项目内把图集取消了,那么所有由于图集引起的问题就都不存在了,打包出去的Assetbundle就变得很小了,然后再把图集标签设置回去,再重新打包Assetbundle,居然发现有时候之前打包变小了的Assetbundle没有再变回巨大的容量,而在运行的时候也没有出错……虽然这好像是好事情,但由于是不稳定的,应该算是一种bug来的,所以不能依靠这种方法来解决问题。
第四个问题,其实不算问题,是Unity对于图片的处理方式,不过对于项目实际发布时的容量会有影响。比如你把一张只有100多k的图片放到项目里面,由于Unity会自动帮你把图片生成为Unity自身的图片格式,通过自身的设置来对图片进行压缩和保存,而导致了100多k的图片进入项目后就变成了差不多1M的容量。这其实没有什么问题的,这样做对于Unity本身来说是一种性能的优化,它可以把图片更快的转换成显卡使用的Texture。但照这个容量打包Assetbundle,会发现原本不大的图片资源,突然就变得大好几倍了。从正常的途径解决,你要么降图片质量,选择图片压缩方式,或者降图片尺寸,把图片变小。但不论你怎么做,对实际的效果还是会有影响的,特别是在ios上面看,色彩位数不够了,渐变色会变成一块一块的。
下面来说说解决的办法。
这些办法我已经实际的应用在项目里面了,暂时没发现什么问题。如果各位还有更好的方法,欢迎指教。
首先,我抛弃了通过升级unity版本来解决以上问题。最近Unity的新版本的恐怖程度应该不用我多说什么了,每次出来一个版本,就伴随着一堆新的bug出现,我怕升级完是跳过了一些坑,但又掉进新的坑里面了。
既然不能靠Unity解决,那么就只能靠自己了。
对于依赖的问题,其实我们也可以完全的不用Unity5的那套新Assetbundle机制,而使用旧版Assetbundle的自己处理依赖的方式,通过push和pop来打包依赖的。虽然这些方法被标记成已过时了,但暂时的版本还能用。不过我同样也抛弃了这样的方式,因为既然是标记成已过时方法,虽然现在还能用,但难以保证会不会在下个版本就被彻底抛弃了,这样风险有点大。
那么还是用Unity5的新机制来打包依赖吧。回头看一看,前面两个问题实际上都是由于图集引起的,所以我决定抛弃Unity的Sprite Packer,而自己写一套图集生成方法。其实我之前已经写过几次图集生成的算法了,这次稍微不同的是,我不想手下的人由于我这样的改动而对原来的制作流程有太大的变化,所以我还是直接让他们用散图的sprite来制作UI,只是在实际打包Assetbundle时,会通过我的方法,生成和Unity的Sprite Packer一样规则的图片图集。具体规则就是在图集最大尺寸可以允许的范围内,把所有散图合成一张png,如果超出了最大尺寸,再把超出的散图打包成第二张、第三张的png。但同一个图集里面的所有散图都会保存在同一个配置文件里面,所以只要加载了图集的配置文件,然后找到具体某个sprite属于哪张png,就可以按配置生成处理,而由于是同一张png的texture2D生成的sprite,他们的draw call也会是一样的。这一步反正挺成功的,我把打包和加载的方式稍微改了之后,使用者估计还不知道自己没有使用Unity的Sprite Packer。不得不说一句,面向对象就是好,边界明确,本来这样的修改会牵涉到很多的,但现在只需要小改就行了。
既然图集打包的问题解决了,那么如果UI上面用了图集又该怎么办呢?由于已经是自己生成的png作为图集,原本打的依赖肯定就不成立了,需要自己去记录一下。我的做法是在打包UI预设的时候,全部遍历查找一下有哪些是用到了sprite的,然后把sprite所属的图集名称和sprite的加载名称记录下来,然后把UI预设另存一份,把sprite去掉。再用另存的UI预设打包Assetbundle。最后在加载该UI的时候,通过配置知道那些组件上面用了哪个sprite,再在初始化的时候帮他加载回来。这一步也挺成功的,由于是完全自动化的,所以制作者根本不需要管过程,只需要按照正常流程做UI就行了。
既然前面两个问题解决了,那么第三个问题也没什么问题了。然后看第四个问题。这个问题其实官方的API上面就有解决办法,就是把图片的后缀改成bytes,这样图片就变成了TextAsset来加载,加载后再通过bytes来重新生成Texture2D。这样做了之后,在生成Texture2D的速度上是比Unity帮你保存的大容量的贴图纹理要慢一点点,但具体慢多少我没感觉出来。然后好处就是,美术给的图片是多大,那么我们打包出来的资源容量就是多大,很准确了。
然后,问题就都解决了。我的项目是把所有资源完全外部加载的,编译内部不包含任何资源,所以这个外部包的大小是很明显能看出差别的。现在项目还没有做完,资源不算特别多。在没有解决那些乱七八糟问题前,外部资源总容量有300多M,压缩了之后有100多M。解决了之后,外部资源变成只有100M左右,压缩了之后只有60多M了。由于现在还处于制作阶段,所有美术资源还没开始整理和压缩容量,相信把美术资源再整理之后,容量就会更小了。