UGUI面试笔试

UI 资源规范(内存优化)

  1. 图集大小限制 任何的 UI 图集最大 size 1024*1024;
  2. 多用九宫格 能用九宫格的尽量用九宫格来减小原图大小;
  3. 尽量缩小原图 美术给过来的 UI 原图 size 尽量小,可参照2的次方缩小或者多张类似图大小可以缩成可放入一个2的次方图集内;
  4. 分开极端长度 对于特别长条的 UI 原图,会使图集非常大的,在制作界面的时候用两个 Image 拼接即可,打出的图集会小很多;
  5. 合并一些图集 图集利用率低于 1/3 的时候,就要考虑和其他低使用率的图集合并;
  6. 复用 UI 资源 尽量复用资源,减少不必要的原图,如一些颜色变化的背景图,可以在使用Image 的时候直接按需求修改顶点色即可;
  7. 关闭 mipmaps unity的mipmap相当于贴图的LOD技术,可以实现根据贴图与摄像机的距离调节贴图像素,缺点是会根据摄像机远近不同而生成对应的八个贴图,占用内存;
  8. 资源分类 同一个界面出现的 UI 资源尽量放到一个图集,重复利用的公用资源放common(DrawCall 优化);
  9. 资源格式
    在这里插入图片描述

GPU 优化

GPU负责整个渲染流水线,从处理 CPU 传递过来的模型数据开始,进行 Vertex Shader、Fragment Shader 等一系列工作,最后输出屏幕上的每个像素。

Shader 优化

  1. 关闭Fog Fog { Mode Off },最早有一个版本我们没有关闭 Fog,导致打开全屏 UI 的时候帧率明显往下掉(我们的全屏 UI 开启后会关闭主相机,理论上渲染压力和CPU 消耗会降低),后来 Adreno Profiler 真机调式 Shader 代码后发现了此问题;
  2. Fragment 剔除 剔除掉 Alpha 为 0 的像素点 减少 OverDraw;
  3. 简化 Shader 代码 单独把效果需求代码拎出来独立一个 Shader;

OverDraw 优化

在每帧绘制中,如果一个像素被反复绘制的次数越多,那么它占用的资源也必然更多。目前在移动设备上,OverDraw 的压力主要来自半透明物体。因为多数情况下,半透明物体需要开启 Alpha Blend 且关闭 ZWrite,同时如果我们绘制像 alpha=0 这种实际上不会产生效果的颜色上去,也同样有 Blend 操作,这是一种极大的浪费。我们的 UI 绘制是 Alpha Blend 且关闭 ZWrite,因此 UI OverDraw 的优化主要是在制作界面的时候减少 UI 重叠层级。除此之外还是有一些我们程序可以控制的优化点:

  1. 对于九宫格的 Image,如果去掉 fillcenter 不影响最后出来的效果就要把fillcenter 去掉,可以减少中间一片的像素绘制;
  2. 看不见的元素且没有逻辑功能要 disable 或者挪出裁剪区域,而不要通过设置Alpha=0 来隐藏;
  3. 不要使用一张 Alpha=0 的 Image 来实现放大响应区域的功能;(alphaHitTestMinimumThreshold设置响应的最小alpha)
  4. UI 底层系统来控制隐藏看不见的元素,例如打开全屏 UI 的时候把下面看不见的 UI 挪出裁减区域、关闭主相机渲染。

CPU 优化

在我们项目开发的过程中,遇到的大部分 UI 相关性能热点都是和 CPU 消耗相关的。大致可以分为以下几类:

DrawCall

DrawCall 是 CPU 调用底层图形接口,频繁的调用对 CPU 性能的影响是很明显的。优化思路很简单,合批绘制。UGUI 本身的动态合批机制会帮我们尽量的去优化合批,我们要做的就是弄清楚它的合批机制然后让 UI 元素尽量合批绘制。

  1. 合理分配图集,同一个界面上的图尽量打到一个图集,多个界面复用的图,放到 common;
  2. 制作界面的时候,相邻节点尽量使用同一个图集的图片;
  3. Text 本身也是用的 Font Texture,不同字体的 Text 也是来自不同的图集,所以在布局界面的时候也要尽量避免穿插打断绘制流程;
  4. DrawCall 的数量不是完全由 Hierarchy 的布局决定,和 UI 的位置也有关系,这个位置不是指的 Rectranform 上面的 size 位置重叠就一定打断绘制,而是真实的三角面的位置是否重叠。这个可以在 Scene 视图下用线框模式(Wireframe)去观察;
  5. 少用 Mask 组件,Mask 实现的原理是 Stencil Buffer,往模版缓存里绘制,模版缓存里的东西才是可见的。模板缓存会打断所有的合批,Mask 的子节点和外面的节点无法合批,模板缓存自己占一个 DrawCall。Unity5.2 之后的版本建议使用 2D Rect Mask 替代;

Canvas.SendWillRenderCanvases()

真机 Profiler 会经常看到这个函数的消耗飙高,研究 UGUI 源码得知该 API 为UI 元素(Graphic 子类)自身发生变化(比如被 Enable、顶点色变化、Text 组件的文本变化等)时所产生的调用,CPU 飙高大部分情况下主要是因UnityEngine.UI.Graphic.UpdateGeometry()重新生成了所有的顶点数据,具体有哪些操作会导致Graphic重新生成顶点数据,可以搜索UGUI的源码(4.6.9)Graphic.SetVerticesDirty()。刷新顶点数据的消耗和当前 Graphic 的顶点数正
相关,所以接下来研究一下 UI 顶点数。对于 UI 来说,顶点数量主要是由 Image控件和 Text 控件产生的。对于 Image 控件,顶点的数量取决于 ImageType。

  1. Simple 模式只有 4 个顶点,Image 能用 Simple 模式的尽量用 Simple;
  2. Sliced 模式,就是我们常用的九宫格,要分两种情况:勾选了 fillcenter 的顶点数是 36 个,没勾选 fillcenter 的顶点数是 32 个;
  3. Tiled 模式,就是平铺模式,这个定点数量就完全取决 Image 控件的Rectranform 设置的大小和原图大小,简单来说就是铺开了 N 张图就是 4*N 个顶点;
  4. Filled 模式,一般用来做进度条、技能 cd 转圈等效果,这个顶点数就取决于Fill Method 和进度值,组合的情况比较多,我就不展开算了,具体数值大家感兴趣的自行 demo 看下,总之不少于 4 个;

其实 UI 顶点数的重灾区主要是在 Text 控件:

  1. Text 在不添加任何效果的情况下是一个字符串是 4 个顶点,
  2. 单独添加 Shadow 后一个字符串顶点数是 8 个,
  3. 单独添加 Outline 后一个字符串顶点数是 20 个,
  4. Shadow 和Outline 都加一个字符串顶点数是 40 个,
  5. 如果界面上有 N 个字符串,那顶点数就是 N*上面的系数。

这个量级是很可怕的。因此对于Text控件尽量不要去添加 Shadow、Outline 效果,特别是Outline!这对 CPU 和 GPU 都很伤。策划和美术想要的文字凸显的效果可以用背景和文
字配色来实现,效果实在无法接受的可以少量使用 Shadow。对于频繁变动的Text 文本(例如聊天),更要慎重考虑是否使用 Shadow、Outline。除了优化 UI 顶点数以外还有一些设置是会影响此函数消耗的,查看源码得知如果勾选了 Canvas 下的 pixelPerfect,在刷新顶点的时候会做一些额外的操作RectTransformUtility.PixelAdjustRect,像素对齐调整,此操作很费,所以不要勾选 Canvas 下的 pixelPerfect。

Canvas.BuildBatch()

理解该函数的消耗之前先来说一下 UGUI 的动态合批机制(减 DrawCall),UGUI是以 Canvas 为合批的根节点,首先 Canvas 与 Canvas 之间是没法合批的,Canvas 下所有节点的顶点数据会生成一张大的 BatchedMesh,再根据材质纹理、渲染顺序等把这个 Canvas 下能合批的 Mesh 生成 SubMesh,一个SubMesh 就是一个绘制批次。每当这个大的 BatchedMesh 中任意一个顶点数据发生变化的时候,UGUI 目前的实现方式很粗暴,就会直接重新生成BatchedMesh , Canvas.BuildBatch() 干 的 就 是 这 个 事 情 。 由 此 可 见Canvas.BuildBatch()消耗飙高的因素有两点:Canvas 下的顶点数量多和Canvas 下的顶点发生变化。

通常之前所提到的 Canvas.SendWillRenderCanvases()的调用都会引起Canvas.BuildBatch()的调用。理解了该函数的原理之后,优化的思路就清晰了:减少一些频繁变动的 UI 元素的 Canvas 下的顶点数量。关于优化 UI 顶点数量上面说的比较多了,接下来重点说下顶点变化。先来看下上面提到的 BatchedMesh该 Mesh 包含了顶点色,所以修改顶点色、缩放、位移等导致 Mesh 数据变化的操作,都会引起 Canvas.BuildBatch()。游戏中难免有 UI 频繁移动、缩放、变颜色的需求,既然一定要变动,那我们就让这些频繁变动的 UI 元素单独放在一个 Canvas 下,且尽量减少这个频繁变动的 Canvas 下的顶点数量。 简单的概括一句优化思路就是:动静分离。当然如果 Canvas 分离得太细也有另外一个问题,DrawCall 数量提升,所以要做一个权衡。

其他 UI 卡顿优化建议

除了以上三点,UI 方面造成 CPU 消耗飙高的原因还有很多:

  1. 精简拆分 UI Prefab 文件 把打开界面瞬间看不见的 UI 元素尽量拆分,功能逻辑触发其他 UI 元素时再动态加载,例如:背包、练鼎根据子页签内容拆分,这样可以减少打开 UI 时卡顿的感觉;
  2. 避免一次性初始化 对于 ScrollRect 里存在非常多元素的界面,避免一次全部 Initiation,回收重复里节点元素,刷新数据即可(可以实现一个公用组件);
  3. 避免频繁 Initiation、Destroy 某个 UI 子元素 类似小地图的 SceneActor 圆点、寻路路径、YLIconBox 的子节点,使用对象池维护起来重复利用;
  4. 序列化保存Prefab的时候,尽量让active的状态和大部分情况下出现的active状态一致,可以避免切换 active 状态带来的性能消耗;
  5. 在 UI 布局位置不会动态调整的情况下,不要偷懒用 Layout 组件实现自动布局,直接 Rectranform 写死坐标位置即可,Layout 动态计算有开销;
  6. UGUI对比 NGUI有个不好的地方是所有的 Graphic 组件默认都是参与接受射线检测,响应 EventSystem 事件的,这样在在长时间按屏幕输入的时候参与遍历检测的 UI 节点会很多,界面复杂到一定程度会导致 EventSystem.Update 消耗飙升。建议用 5.2 之后版本的可以让所有没有响应需求的 Graphic 组件Raycast Target 默认不勾选,4.x 也是可以添加 Canvas Group 然后去掉 Block Raycasts 和 Interactable;
  7. 不要使用 Text 的 Best Fit 选项,有额外开销;
  8. 对部分 UI 有频繁显示和隐藏需求的,建议不要直接调用 SetActive,上面有提到这样会导致顶点数据重新生成,过于频繁调用有性能问题。有两个建议:挪出裁减区域或者禁用 CanvasRender,具体采用什么方案要看需求,因为禁用CanvasRender 还是会响应事件检测。
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fagetta

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值