《Unity3D高级编程之进阶主程》第四章,UI(七) - UI优化(二)

前文回顾 UI(一)

    对NGUI和UGUI进行了比较,讲述了如何选择UI系统作为项目的UI框架。

前文回顾 UI(二)

    UGUI的原理,以及组件使用详解。

前文回顾 UI(三)

    UGUI源码中输入事件模块源码剖析。

前文回顾 UI(四)

    UGUI渲染核心源码剖析。

前文回顾 UI(六)

    如何在游戏项目中架构UI框架。

前文回顾 UI(七)-优化(一)

    优化UI的几种方法,UI动静分离,拆分过大的UI,UI预加载

这篇我们来继续聊聊优化,UI图集Alpha分离,UI字体拆分,Scroll View 滚屏优化,以及UGUI图在改变颜色或Alpha后,导致对Mesh重构的优化。

===

④ UI图集Alpha分离。

为什么要对UI图集进行Alpha分离?

在项目制作完成后,对项目进行打APP包时,会发现包体大小并不适合运营,因为APP包体太大,导致用户下载的意愿减低,首先下载率就会受到影响,其次APP包体太大会导致卸载率的增大,进而导致次日留存,3日留存,7日留存率的降低,所以运营人员时常要求控制APP包体大小到XX 兆以内。

UI图集的压缩是减少APP包体大小的一部分,也是比较重要的一部分。包内的资源,以UI图集为主要组成部分,压缩UI图集是必要的手段。

对图集进行压缩后,在屏幕上显示却不尽如人意,模糊,锯齿,线条等劣质的画面出现,这是因为我们在使用压缩模式ECT或PVRTC时将透明通道也一并压缩进去了,导致了渲染的扭曲。所以我们要把透明通道alpha分离出来单独压缩,即压缩了图集,又不失真。

如何分离UI图集的Alpha呢?

这里主要是针对NGUI的方案,而UGUI由于是内部集成的,Alpha分离在目前版本的UGUI中已经帮你完成了,所以我们针对NGUI来讲一讲。

首先,用TexturePacker在打图集时将原来打成2张的图集,改成打成一张RGB888的png和一张Alpha8的png。RGB888的PNG图没有alpha,而所有的alpha通道都在Alpha8的PNG里。

然后,我们需要改下NGUI的shader,把原来的只绑定一张主图的shader改成需要绑定一张主图和一张Alpha图的shader。

需要修改哪些呢,需要修改下面这4个内容

    Unlit – Transparent Colored.shader,

    Unlit – Transparent Colored 1.shader,

    Unlit – Transparent Colored 2.shader,

    Unlit – Transparent Colored 3.shader

修改的内容很简单,加入_AlphaTex ("Alpha (A)", 2D) = "black" {}变量,用来可以绑定Alpha图。

然后在frag函数中,有对alpha与主图alpha操作的内容,都替换成Alpha图的alpha值。

这样一来,就启用了Alpha图的Alpha,而主图还是承担了主要色彩内容。

最后,以上都完成后,选中一个创建好的图集prefab会发现Inspector窗口下的预览窗口以及Sprite选择窗口中看到的sprite都是没有alpha通道的,这是因为用于显示的texture是atlas.mainTexture,这个mainTexture就是那张RGB888的图集。

我们可以在编辑器模式下动态生成一个rgba32的texture来替换它,rgb和alpha通道的值分别取自rgb888图集和alpha8图集。

这需要修改一下NGUI的编辑类,如下这几个类。

    UIAtlas.cs,

    UIAtlasInspector.cs,

    SpriteSelector.cs,

    NGUITools.cs,

    UISpriteInspector.cs

修改以上类里,绘制图片时都启用新生成的图,就是上面所说的,用RGB888和Alpha合成的临时图。

其实修改的部分并不多,修改的方向和原理也简单,一是 Shader 的 alpha 来源修改为新的alpha图,二是 Shader修改导致编辑时有显示问题,需要在编辑器部分生成临时的图来替换原来显示的图。

⑤ UI字体拆分。

为什么要拆分UI字体?

项目中,字体其实占了很大的空间,如果有几个不同的字体一起展示在屏幕上,会在展示的一瞬间出现比较严重的卡顿现象。

如果在特定的场景内,我们需要更快的速度,而对字体需求又不是那么严格,那么我们可以拆分字体,让加载字体的速度更快,让场景加载的速度更快。

如何拆分UI字体?

因为特殊的原因,某场景中,对字体的需求不是那么严格,但又必须有字体的支持,这时我们就要拆分字体。

把字体中的常用字拆出来,另外生成一个字体文件,让字体文件变小,加载变快。

比如在登陆场景中,我们只需要几个数字和字母,所以我们大可以从字体中提取数字和26个字母成立一个新的字体在场景中应用。这样登陆场景就省去了大的字体的加载。

又比如,注册登陆后,取名字的场景,我们既要对取名字进行限制,也需要对场景加载速度进行控制,这时用拆分UI字体的方法就恰到好处。将字体中常用的3000字拆出来,生成新的字体进而用到场景中去。

注意,这种拆字体,是为了特殊场景所用的,这其实是一种用空间换时间的方法,增大了总体存储空间大小,使得在读取文件时减少了CPU消耗。

⑥ Scroll View 滚屏优化。

Scroll View 使用在类似背包的界面中非常常见,会有巨量的元素存在在窗口中进行渲染,所以在生成和滑动时,会消耗巨量的CPU来重构Mesh,进而导致游戏运行缓慢,出现卡顿现象。

要优化这种情况,就必须对滚屏菜单组件进行改造,将原来的所有元素都必须实例化的问题,改为只实例化需要显示的实例数量。

然后在拖动滑动的期间,实时判断是否有看不到的UI元素可以重复利用,将他们填补到需要显示的位置的上去,再对该单位元素的属性设置成本该在这个位置上显示的信息,让它显示为在该位置需要显示的元素的样子。

表现上看起来如同下面所描述的,

    在窗口中有10排元素显示在那里,其中5排是展示在中央的窗口上的,顶上2排因为超出了窗口无法可见,

    同样的下面3排也是因为超出了窗口无法可见,在整个10排元素,整体向上滑动期间,顶上2排变成了3排,底下变成了2排,

    其中最顶上的1排超过了重置的位置,于是就移动到了下面去了,这样整体10排元素,又变成了顶上2排,底下3排的局面,

    这样不断反复,不断在移动顶上或底下的1排元素,把他们移动到需要补充的位置上去,

    看起来像是,很顺畅地可以上下滚屏整个500个元素,实际上只是对这十几个或者几十个元素在不断重复的利用而已。

这个 Scroll View 自定义组件是项目中必须的,大部分项目都会遇到这类问题,有一个自己优化过的自定义组件,能很快很高效的解决这类问题。

⑦ UGUI图在改变颜色或Alpha后,导致对Mesh重构的优化。

首先需要解释下为什么在UGUI的图元素在改变颜色或Alpha后会导致Mesh的重构?

UGUI的Mesh的合并机制,是拥有相同的材质球的Mesh合并在一起才能达到最佳效果,一个材质球对应一个图集,所以在相同图集内的图片才需要合并在一起。

当元素需要对颜色进行改变时,UGUI通过改变顶点的颜色来实现颜色的变化,改变当前元素的顶点颜色后,需要合并到整块的Mesh里去,因为不能直接从原来合并好的Mesh上找到当前的顶点位置,所以需要一次整体的合并重构Mesh。

如果元素改变了 alpha 会更糟糕,改变alpha的效果无法通过改变顶点的颜色来实现,于是需要拆分出一个另外的材质球来进行渲染,通过对材质球的参数改变来实现 alpha 的效果。

这样做不但重构了Mesh,还多出来个材质球,就相当于多一个Drawcall,效率消耗相当大。

倘若在动画里,每一帧都对UGUI的颜色和Alpha进行改变,那么UGUI每一帧都会对Mesh进行重构一次,并且每帧都生成新的材质球来实现 alpha 的透明效果。

这样做消耗了大量的CPU运算,通常使得UI界面在运行动画时效率低下。

如何对此做优化?

我们不希望在UI颜色改变时,导致Mesh重构,这样动画中消耗掉太多CPU,

那么我们就自己建一个材质球,提前告诉UGUI:我们使用自己的特殊的材质球进行渲染。

然后,当颜色动画对颜色和 alpha 更改时,我们直接对我们自定义的材质球进行颜色和 alpha 的改变。

这样UGUI就不需要重构Mesh了,因为把渲染的工作交给了新的材质球,而不是通过 UGUI 设置顶点颜色和新材质球来达到效果。

如何操作?

首先,我们需要把UGUI的Shader下载下来。

然后,建立一个自己的材质球,并且材质球里使用下载下来的UGUI的Shader。

再次,把这个材质球放入Image或RawImage的Material上去,与Image或RawImage绑定。

接着,写个类比如class ImageColor继承MonoBehaviour,里面有个public 的颜色变量,比如public Color mColor,类里面只干一件事,在update里一直判断是否需要更改颜色,如果颜色被更改,就把颜色赋值给Material。

最后,把动画文件中的颜色部分从更改Image或RawImage的颜色变为更改 ImageColor 的颜色变量。

这样UGUI颜色动画在播放时,不会直接去改变 Image 或 RawImage 的颜色,改变的是我们创建的 ImageColor 的颜色。

通过 ImageColor 来改变材质球属性,最后达到不重构Mesh的效果。

不过要注意下,因为启用了自定义的材质球,当 alpha 不是1的时候,会与原有的UGUI产生的材质球的透贴形成不同的渲染排序,

因为当两张透贴放在一起渲染时,会因为Shader的渲染排序错误而前后不一致。

这个问题是半透明物体的排序问题,归根结底是无法写入深度数据,是3D渲染中无法彻底解决的问题。

像解决其他半透明排序问题一样,可以通过改变自定义的 Shader 渲染次序(RenderQueue)来解决。

下篇将继续聊聊 UI 优化。

感谢您的耐心阅读

Thanks for your reading

  • 版权申明

    本文为博主原创文章,未经允许不得转载:

    《Unity3D高级编程之进阶主程》第四章,UI(七) - UI优化(二)

    Copyright attention

    Please don't reprint without authorize.

  • 微信公众号