news 2026/9/30 1:19:36

UGUI重建流程和优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
UGUI重建流程和优化

UGUI重建流程和优化

参考文献
(五)UGUI源码分析之Rebuild(布局重建、图形重绘)_ugui rebuild-CSDN博客
(99+ 封私信 / 83 条消息) UGUI源码解析(二十一)LayoutRebuilder - 知乎
(99+ 封私信 / 83 条消息) UGUI源码解析(五) CanvasUpdateRegistry - 知乎
(99+ 封私信 / 85 条消息) UGUI UI重建二三事(一) - 知乎
(99+ 封私信 / 85 条消息) UGUI UI重建二三事(二) - 知乎

总体流程简述

首先,我们对UI进行修改时,如修改其大小,改材质等很多情况下,UI会将自己标记为脏,然后放进一个队列中。在相机即将渲染时会处理这个队列的元素,进行布局的重新计算,称作布局重建。然后重新生成graphic的网格,称为网格重建。

重建过程的主要接口

ICanvasElement接口

标记此组件需要参与重建。主要是Rebuild方法,会在此节点需要重建时调用,参数表面重建过程。
CanvasUpdate.// 标记了重建过程
Prelayout,
Layout,
PostLayout,
PreRender,
LatePreRender,
MaxUpdateValue,
LayoutComplete在布局完成时调用
GraphicUpdateComplete在网格重建完成时调用

实现情况:

1.Graphic实现,用来生成网格。即网格重建过程。
2.LayoutRebuilder实现,用来进行布局。即布局重建过程。
3.InputField、ScrollRect、Scrollbar、Slider、Toggle实现,主要是根据重建过程实现自己的功能。

ILayoutElement接口

标记此节点需要参与布局重建。给出布局重建时节点的宽高参数。
有宽高的min,preferred,flexible,用于布局。
此外layoutPriority标记布局优先级。
CalculateLayoutInputHorizontal
CalculateLayoutInputVertical
这两个方法计算自己的理想宽高。

实现情况:

Image、Text、InputField实现preferredWidth,preferredHeight会返回最合适的大小。
ScrollRect实现了所有参数,但都返回-1,仅供布局系统调用。
LayoutElement实现了所有参数,并开放到编辑器供配置。

ILayoutController接口

设置子节点的位置宽高。
实现两个方法SetLayoutHorizontal、SetLayoutVertical。作用是设置自己的子节点。

实现情况:

有三个类实现GridLayoutGroup,HorizontalLayoutGroup,VerticalLayoutGroup,

重建框架执行流程

CanvasUpdateRegistry处理重建的类。

待重建元素列表

m_LayoutRebuildQueue保存需要更新布局的队列
m_GraphicRebuildQueue保存需要更新图形的队列
队列元素的类型都是ICanvasElement,此接口为布局元素的基类。

元素如何加入到重建列表?

向布局重建队列添加元素的方法为
CanvasUpdateRegistry.MarkLayoutForRebuild //将需要重建的元素加入重建列表。实际调用下面的
CanvasUpdateRegistry.MarkLayoutRootForRebuild //将需要重建的布局根节点加入重建列表
一般情况下,各个组件在布局需要修改时调用MarkLayoutForRebuild将自己加入重建列表。
比如 OnEnable、OnDisable、OnRectTransformDimensionsChange等
向图形重建队列添加元素的方法为
CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild

重建总入口

CanvasUpdateRegistry实现单例模式,单例的构造函数就向Canvas.willRenderCanvases注册了PerformUpdate函数,因此PerformUpdate为重建过程的总入口。

PerformUpdate执行逻辑

首先调用CleanInvalidItems清理无效布局组件。
m_PerformingLayoutUpdate标记了正在进行布局重建。
使用SortLayoutList排序。排序依据为父物体层数少的在前。
依次对m_LayoutRebuildQueue列表中元素调用Rebuild,参数分别是Prelayout、Layout、PostLayout。
这就等于进行了重建的三个步骤。其中Layout进行了实际布局重建。
最后调用每个元素的LayoutComplete方法。
清理m_LayoutRebuildQueue列表。
布局重建完成,然后进行网格重建。此过程与上面布局重建基本一致。
先调用ClipperRegistry.instance.Cull(),不用排序
依次对m_LayoutRebuildQueue列表中元素调用Rebuild,参数分别是PreRender、LatePreRender。
最后调用每个元素的GraphicUpdateComplete方法。
清理m_GraphicRebuildQueue列表。
可以看到基本就是依次通知每个元素重建了。具体行为是让组件自己实现的,即调用Rebuild函数,每个组件都会重写来实现不同行为。

重要组件的具体重建行为

Graphic.Rebuild 网格重建

实现网格重建,即生成图片和文本网格的部分
Graphic会在PreRender时,检查网格刷新,检查材质刷新。
生成网格方法是OnPopulateMesh。
值得注意的是网格生成后,可通过IMeshModifier对网格进行调整,实现网格特效。如Shadow。
Graphic基类中网格生成是直接创建面片显示纯颜色。
Image重写了OnPopulateMesh方法,实现了一些特殊填充,就是Sliced,Tiled那些。
(这部分实现没有技巧,全是硬编码)
Text重写了OnPopulateMesh方法,改成文本的网格生成。其实现未开源。
不过可以通过m_TempVerts访问到每个字符生成完毕的网格数据。(可以用这个做超链接)
RawImage实现和Graphic基本一致,区别在没有主贴图时不会生成网格。

ILayoutElement.Rebuild布局重建

所有加入布局重建中元素,都是RectTransform,加入列表时会包一个LayoutRebuilder。
LayoutRebuilder在Rebuild的Layout阶段时计算了自己理想宽高。过程:
PerformLayoutCalculation递归后续遍历所有子节点。即先子节点,再自己。会对每个ILayoutElement节点执行一个委托CalculateLayoutInputHorizontal,作用是计算自己的最终宽高。
随后PerformLayoutControl递归后续遍历所有子节点。即先子节点,再自己。会对每个ILayoutController节点执行一个委托SetLayoutHorizontal,作用是设置自己的子节点。
特别的,有时需求会需要我们获取布局完成后的组件位置,可调用这个方法立即进行此元素的布局重建。之后可正确获取最佳宽高值。
LayoutRebuilder.ForceRebuildLayoutImmediate
其实现是,创建一个此节点的LayoutRebuilder,然后以CanvasUpdate.Layout为参数立即调用一次Rebuild。也就是立即触发一次布局重建。调用参数为布局根节点。

总结LayoutGroup的重建过程

包括GridLayoutGroup,HorizontalLayoutGroup,VerticalLayoutGroup
调用CalculateLayoutInputHorizontal时,LayoutGroup会收集所有子物体,保存在m_RectChildren中。有ILayoutIgnorer且ignoreLayout都是false的除外。
HorizontalLayoutGroup中重写CalculateLayoutInputHorizontal,计算自己宽高。
SetLayoutHorizontal执行过程
随后SetChildAlongAxisWithScale时设置子节点位置。

优化思路

可修改源码,检查上文提到的两个队列来查看重建情况。
重建优化思路基本就是减少重建的触发,以减少重建次数。
即减少UI元素位置大小图片材质等修改,减少mask矩形区域的变更。
少用布局组件,不会变化的布局组件删除或者关掉。不要频繁修改布局组件元素。

OnRectTransformDimensionsChange

可观察到此函数在网格需要变化时触发布局重建。
如修改Anchor,AnchoredPosition,Pivot,SizeDelta大概率导致网格变化产生重建。
而如果仅改变Scale,Rotation,Position,不会发生重建。
因此可考虑用scale改变代替enable避免重建。

摘抄大佬的笔记,总结触发rebuild的情况

https://zhuanlan.zhihu.com/p/448293298
  1. Text控件 文本的内容及颜色变化、设置是否支持富文本、更改换行模式、设置字体最大最小值、变更文本使用的对齐锚点、设置是否通过几何对齐、变更字体大小、变更是否支持水平及垂直溢出、修改行间距、变更字体样式(正常、斜体.....)。
  2. Image控件 颜色变化、变更显示类型(Simple、Sliced、Tiled、Filled)、变更是否应保留Sprite宽高比(Image.preserveAspect属性的变更),FillCenter属性变更(是否渲染平铺或切片图像的中心)、变更填充方式(Horizontal、Vertical、Radial360....)、变更图像填充率(fillAmount)、变更图像顺逆时针填充类型(Image.fillClockwise)、变更填充过程的原点(Image.FillOrigin)。
  3. RawImage控件 设置Texture、变更纹理使用的UVRcet、
  4. Shadow效果 改变效果的距离(effectDistance)及颜色(effectColor)、变更是否使用Graphic中的Alpha透明度(useGraphicAlpha)。
  5. Mask控件 设置是否展示与Mask渲染区域相关的图形(showMaskGraphic),enable发生变化
  6. 所有继承MaskableGraphic的控件(Image、RawImage、RectMask2D、Text) 设置此图形是否允许被遮盖、enable发生变化、父节点发生变化(TransFromParentChanged)、在Hierachy面板上发生改变(HierachyChanged)。
  7. 所有继承自BaseMeshEffect的效果类(目前只看到Shadow及PositionAsUV1)的enable变化及应用动画属性的操作。
  8. 所有继承自Graphic的UI控件材质(material)发生变化。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/29 11:47:46

动态图表自由切换,R Shiny多输入控件协同设计全解析

第一章:R Shiny 的多模态图表交互控件在构建数据驱动的 Web 应用时,R Shiny 提供了强大的能力来实现动态、可交互的可视化界面。通过整合多种输入控件与图形输出,开发者可以创建支持多模态交互的仪表板,使用户能够实时探索复杂数据…

作者头像 李华
网站建设 2026/9/30 2:27:19

基于单片机的视力保护器设计

一、系统设计背景与总体架构 随着电子设备普及,青少年近视率逐年攀升,长时间近距离用眼、不良光照环境是主要诱因。传统视力保护手段依赖人工提醒,缺乏实时性与准确性。基于单片机的视力保护器,能自动监测用眼环境并及时干预&…

作者头像 李华
网站建设 2026/9/29 15:20:10

WebSocket 协议详解:ws 和 wss 的区别与应用

WebSocket 协议详解:ws 和 wss 的区别与应用 WebSocket 是一种全双工通信协议,允许客户端和服务器之间建立持久的双向通信连接。它是 HTML5 标准的一部分,旨在解决传统 HTTP 协议在实时通信中的局限性。本文将详细介绍 WebSocket 的工作原理…

作者头像 李华
网站建设 2026/9/29 16:31:50

【Matlab】基于图像处理的苹果质量检测分级系统

苹果质量检测分级系统可以利用图像处理技术来实现。下面是一个简单的基于MATLAB的示例代码,用于检测苹果的质量并对其进行分级。 % 读取苹果图像 appleImage = imread(apple.jpg);% 转换为灰度图像 grayImage = rgb2gray(appleImage);% 对图像进行平滑处理,减少噪音 smoothe…

作者头像 李华
网站建设 2026/9/29 15:49:48

从零构建高质量纹理管线:5个专业团队都在用的行业标准流程

第一章:从零构建高质量纹理管线在现代图形渲染中,纹理是赋予3D模型真实感的关键元素。构建一条高效且可扩展的纹理处理管线,不仅能提升视觉质量,还能优化资源加载与内存占用。纹理格式的选择 不同的应用场景需要权衡压缩比、画质和…

作者头像 李华
网站建设 2026/9/30 8:19:50

【紧急避坑】:低代码项目中事件冒泡失控的6大诱因及应对策略

第一章:低代码组件的事件概述在低代码开发平台中,组件事件是实现用户交互与业务逻辑联动的核心机制。通过监听和响应用户操作(如点击、输入、选择等),开发者可以在无需编写大量前端代码的前提下,构建动态且…

作者头像 李华