四、动静分离
动静分离就是说同一个界面下的UI,可活动的元素(血条,蓝条,伤害数字,CD)放在一个Canvas下,不可活动的元素(人物头像,商城,和平,抽奖等按钮)放在另一个Canvas下。虽然两个Canvas打断了合批,但是却减少了网格的重建时间,总体上是有优化的。
为什么要避免网格重建那? 如果重建的网格过大,就会很卡。比多一个drawcall还卡。
假如同一个Canvas里面有100个Image,Canvas将会把100个单独的Mesh合并成一个大的ShareMesh,用于渲染。
如果刚好这100个Image都是使用了相同的图片或者是同一个图集里面的图片,那么由于使用的Mesh只有一个(ShareMesh),假如UI元素非常多,ShareMesh可能有2M大小(这是一个比较极端的例子,实际上一般的ShareMesh都是几百K,出现这么大的ShareMesh本身就需要注意了),材质球都是同一个(内置的默认材质球),贴图也是同一张,所以得到的结果就是,DrawCall只有一个。
如果这个100个Image是不会动的,合并ShareMesh没有太大的问题
但是如果是上面的血条,蓝条,名字板,CD,文字聊天信息这些需要动的UI元素和其他UI元素合并同一个ShareMesh的,那么问题就来了,血条由长变短,顶点发生了改变,导致2M的整体ShareMesh也需要重新的生成顶点信息,并且整个ShareMesh重新渲染,这明显是得不偿失的。
当然动态的UI还包括文字部分,其中Text是UGUI产生顶点数量的重灾区,一个字符产生4个顶点,如果再加上Shadow则相当于又把Text复制了一遍产生8个,
Outline则会将Text复制4遍产生20个顶点。所以Shadow、Outline不但会产生额外的OverDraw外还会产生过多的顶点数,
一定慎用,确实需要请选择用图片背景替代,其次选择相对较省的Shadow。
五、动态图集
所谓动态图集,就是图集是在游戏运行过程中生成的,而不是在游戏制作阶段生成的。程序生成一张或n张较大的Texture图集(根据项目需求,可以设置Texture大小为1024*1024或2048*2048等等),Image加载进来的Texture不直接使用,而是将Texture信息按照一定规则和排列拷贝到这张Texture上,以达到n个Image共用一张Texture,合并Draw Call的目的,同时Image销毁不用之后,Texture信息从大的Texture图集中移除,达到图集空间重复利用的目的。
什么时候需要打动态图集那?
我们先来看看静态图集的缺点:
1、如果某个界面只需要出现一个图集中包含的小图标(比如掉了一个物品,学会某种技能),这个小图片是包含在一个大的静态图集里的,那为了显示一个很小的图标,就得加载一个很大的图集,从而导致打开该界面很慢(由于加载大的图集)。
并且后期热更新的时候,如果修改或者添加一张图片资源,那么就意味着要更新整个图集的资源。
2、那打成多静态个图集那?这样只有一个图标的界面没问题了。有多个图标出现的界面就有问题了,加载的也多,drawcall也多,并且为了合并Draw Call不被打断,要小心地设置UI的层级,避免ABA问题造成合批被打断,操作复杂度就提高了。
物品图标打了多个图集
一个界面显示多种物品图标
下面demo只是简单的演示一下动态图集的主要思路,图片分配算法也只是将大图片分成64x64的一个一个分区,每个分区采用引用计数开控制是否在使用图片,用于维护整个UI系统的话,这种算法并不适用,但是如果只是用于icon图标的话,由于icon图标是固定尺寸的,所以这套算法就很合适了。
动态图集的缺点:当然动态图集也是有缺点的
1、动态图集因为将图集的生成过程延迟到了游戏运行时,所以必然会比静态图集多了图集生成的成本,当然这也是可以优化的。
2、并且在目前的动态图集生成方案中,还没有出现公开的支持压缩的动态图集解决方案。
3、还有一点,静态图集由于图片在生成过程中是确定的,可以将分配算法做得很好,图集的利用率也能做到很高。动态图集由于图片是动态生成的,在游戏运行过程中也会动态的增减图片,类似操作系统的内存分配算法,图集必然会出现碎片,图集的利用率也不可能做得很高
总之, 如果能把以上五个方面都做到了,那么UI的优化就不成问题。当然有的项目不需要都做到,做到其中几部分就可以了。也有的项目需要做的比 上面的还多。这都得具体问题具体分析,如果游戏中3D场景,人物,特效部分优化做的好,那么UI的压力就小点,如果3D部分优化的不好,那么UI的压力就大点,需要优化的东西就多些。