当前位置: 首页 > news >正文

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)发生变化。
http://www.gsyq.cn/news/104999.html

相关文章:

  • 【流程】——Wordpress零代码快速建站
  • 【EF Core迁移避坑宝典】:解决模型与数据库不一致的终极方案
  • 【Symfony 8微服务架构新纪元】:手把手搭建高可用服务注册中心
  • 揭秘低代码PHP组件事件触发:3个你必须知道的设计模式
  • 基于单片机的城市交通控制系统的设计
  • 2025年昆明黄金店推荐:国民金匠只做黄金,藏着温度与匠心的黄金优选品牌 - charlieruizvin
  • 用计算机图形学优化服装定制与尺寸算法
  • 企业级系统集成性能瓶颈(90%团队忽略的互操作资源消耗黑洞)
  • 揭秘医疗系统日志漏洞:如何用PHP构建不可篡改的访问审计体系
  • qubit初始化失败?90%开发者忽略的3个关键参数配置
  • 计算机毕设java峰数公司医疗设备管理系统 基于 Java 的医疗设备信息化管理系统设计与实现 Java 技术驱动的医疗设备管理平台开发
  • 2025企业微信AI自动打标签实战指南:怎么用AI给客户打标签?需要第三方工具吗?
  • WebSocket 协议详解:ws 和 wss 的区别与应用
  • 【Matlab】基于图像处理的苹果质量检测分级系统
  • Python+Selenium+Pytest+POM自动化测试框架封装
  • ROS系统URDF机器人建模
  • 【低代码PHP组件更新机制揭秘】:掌握高效迭代的5大核心策略
  • 变量传递效率提升80%!你不可不知的R-Python内存管理秘诀
  • 【PHP 8.6扩展开发终极指南】:掌握高性能扩展编写核心技术
  • 助力在线教学提质增效,“魔果云课”以核心功能直击教师痛点
  • 你不知道的纤维协程调度内幕:90%开发者忽略的优先级抢占机制
  • stdin输入流+stdout输出流+stderr错误流 - jerry
  • Cursor 实战指南——Plan、Command、Rule 三大核心功能深度解析
  • 十九、自己搭建frp实现内网穿透
  • 错过这8个R语言质控要点,你的生物数据分析可能全白做
  • 杨建允:AI搜索趋势对互联网营销的影响
  • 环形链表问题
  • 从卡顿到秒级响应,农业传感器PHP数据写入优化全解析
  • 影刀RPA图片上传革命!亚马逊商品图片批量上传,效率暴增2000% [特殊字符]
  • 基于51单片机的无线鼠标实验设计