Unity UI优化(四) - 其他UI优化技术和提示
Unity UI优化(四) - 其他UI优化技术和提示
这篇文章中给出了一些能够帮助提升UI性能的建议,但其中的某些建议在结构上“不整洁”,可能会导致维护困难或者一些其他副作用。
基于RectTransform的Layout
Layout组件的性能开销比较到,因为每当它们的子元素被标记为脏元素时它都要重新计算子元素的尺寸和位置(参考Graphic重建和[Unity UI系统基本原理](https://unity3d.com/cn/learn/tutorials/topics/best-practices/fundamentals-unity-ui)。如果给定的Layout中元素数量少且固定并且Layout结构相对简单,那么可以使用基于RectTransform的布局替代Layout。
通过为RectTransform分配锚点(anchor),可以让RectTransform的尺寸和位置根据父节点进行缩放。例如,可以使用两个RectTransform实现一个只有两列内容的简单布局:
- 左边列的锚点应该设为:X(0, 0.5) Y(0, 1)
- 右边列的锚点应该设为:X(0.5, 1) Y(0, 1)
RectTransform的尺寸和位置的计算由Transform系统自身的原生代码驱动。这要比依赖Layout系统来计算性能更高。可以通过编写MonoBehaviour类来建立基于RectTransform的布局,但是这样实现起来相当复杂,超出了本文的范畴。
禁用画布
在显示或隐藏UI中不连续的部分时,常见的做法是在UI的根节点启用或禁用GameObject,这样可以确保被禁用的UI组件不会收到输入回调或Unity回调。
但是,这样做会导致画布丢弃它的VBO数据。重新启用画布需要画布(及其子画布)执行重建和重新批处理过程。如果这一操作很频繁,CPU占用可能会导致程序帧率低。
一个可行的解决办法是,将需要显示/隐藏的UI放置到它们专用的画布或子画布上,然后只启用或禁用这个画布组件。
这样可以使UI的网格不进行绘制,但它们仍然会驻留在内存中,并且其原始批处理会被保留。进一步将,在这一UI层级中不会由OnEnable和OnDisable回调。
需要注意的是,这样做并不会禁用被隐藏的UI上的任何MonoBehaviour,这些MonoBehaviour仍然会收到Unity的生命周期回调,比如Update。
要避免这一问题,以这种方式实现隐藏的UI上的MonoBehaviour不应该直接实现Unity的生命周期回调,而应该去接收它们的UI根节点的自定义的“CallbackManager”的回调。当UI被显示和隐藏是,这个“CallbackManager”应该收到通知,并决定是否传播生命周期事件。
分配事件相机
如果Canvas的渲染模式为 World Space 或者 Screen Space - Camera 并且使用了Unity内置的InputManager,一定要为其设置合适的EventManager/RenderCamera属性。在脚本中,这两个属性都通过worldCamera属性来设置。
如果没有设置这个属性,UI系统会通过在Tag为Main Camera的GameObject上寻找Camera组件来查找主相机。这一查找操作在每个World Space或Camera Space画布上至少发生一次。由于 GameObject.FindWithTag的查找速度很慢,强烈建议在初始化时为World Space和Camera Space画布设置相机。
在Overlay画布上不存在这一问题。
UI源代码定制
UI系统的源代码托管在Bitbucket,可以修改UI系统的C#源代码并编译成DLL覆盖Unity中附带的UI系统DLL来实现自定义优化内容。