UGUI源码之绘制初探
在日常开发过程当中,类似于HUD或者是弹幕之类大量的体积小但是不断在移动的UI,在不断的重建的过程当中会产生大量的GC,导致游戏卡顿到不能玩。
今天就研究一下Unity中UGUI的绘制方法以及规则:
UGUI的源码地址(C#部分):
下载地址
利用底层API进行绘制
首先我们要搞清楚,到底UGUI是如何对UI进行绘制的。我们首先从最稀疏平常的Image开始。
Image源码尝试阅读
我们首先盯上的是Image,我们发现首先,其基类为MaskableGraphic,其他的接口我们先暂时不管,并且最终我们看到其最终的基类依旧是Monobehavior。
经过阅读,其实最主要的绘制部分其实不多,主要是对Graphic基类中几个成员对象的重写。
- mainTexture属性 主要负责提供贴图
- material属性 主要提供材质
- OnPopulateMesh函数 主要提供顶点以及UV信息(也就是Mesh信息)
首先是mainTexture,我们发现就是简单从sprite中取出Texture而已,如果没有sprite则提供默认的白色贴图
然后是material属性,我们可以看到,也是非常简单,首先判断有没有材质,如果没有就提供默认材质。
最后就是比较重头的OnPopulateMesh,这个函数的主要作用就是用于构建Mesh。
在这之前我们可以先看看VertexHelper这一个类的作用。
打开VertexHelper的源码,我们可以很清楚看到,其实VertexHelper只是一个临时的容器,用于存放需要渲染的Mesh信息。
值得注意地是其中所用到的ListPool,性能高度敏感的代码块,需要大量的使用对象池,这是优化代码效率减少GC的好技巧,无论是网络模块还是底层的组件重用,对象池都是必不可少的!
那这个时候我们其实自己可以想到,我们的合批需要怎么做了,在我们使用UGUI的时候其实每一个Image都是使用了PopulateMesh来进行Mesh生成之后进行合批,我们其实可以将这一步直接放在这一个函数里面,由我们自己来重写就可以了,这样我们就不需要调用N次PopulateMesh之后进行合并,而只需要调用一次自己编写的PopulateMesh函数就可以了。
我们再看一看Graphic基类当中对该函数的编写:
上面图中我们可以看到,其仅仅是画了一个正方形的Mesh而已。
具体如何通过Vertex画Mesh其实和OpenGL中VAO EBO是一样的,就不再赘述了。
我们回过头来看Image当中的OnPopulateMesh:
我们会发现,当Sprite为空的时候画Image的方式就是Graphic,与我们平时的使用方式相同。
当Sprite不为空则会通过不同的sprite类型来进行绘画,实际上就是获取不同的UV,通过何种方式来将贴图绘制到Mesh上面。
我们这里就只看一下最简单的sprite绘制方法,我们会看到
其实也是画个矩形……
GetDrawingDimensions在获取Rect,而通过GetOuterUV来获得UV。
获得UV的时候主要是为了将Sprite中空白的地方进行裁切,主要是通过从Native层得到的padding来计算的,具体内部如何计算我们不得而知,但是我们拿到padding值就已经可以完全算出Sprite的实际大小了。
由于我们只是绘制Simple类型的sprite,所以对UV不需要进行比较复杂的计算,实际上在其他几种绘制方式之中,除了OuterUV还会用到InnerUV,详细算法也就得自己慢慢看了……
至此,我们已经大致明白了如何通过顶点来对UI进行绘制。是时候自己实践了。
自定义Graphic
我们建立一个新的类:MyGraphic
其中我们重写三个对象,也就是上面提到的:
- mainTexture属性
- material属性
- OnPopulateMesh函数
因为自己很懒惰,所以直接将DrawingDimension函数拿过来用了。
获得了Rect以及UV之后我们就可以像普通的Image一样绘制出图片了,
当然我们也可以把其中一句AddTriangle删除,我们就可以发现,我们只绘制了一个三角面
UGUI底层绘制规则
接下去,我比较好奇的是,OnPopulateMesh到底是什么时候进行调用的,因为我们都知道,在UI当中消耗比较大的用于都是UI的重建操作,如果我们能够搞明白UI重建规律的话那我们应该可以大大避免UI重建的情况。
Graphic当汇总的DoMeshGeneration调用了OnPopulateMesh
我们可以看到,当RectTransform的尺寸为0的时候就不再调用OnPopulateMesh了,与以前总结出来的优化方案相同。
下面则是对Mesh进行调整并且通过VertexHelper将数据实装到Mesh当中最后将Mesh赋值给CanvasRenderer进行渲染,这下能理解为何所有显示组件都要带一个CanvasRenderer了吧。
UpdateGeometry调用到了DoMeshGeneration
除了Text在字体变换的时候会进行更新以外,入口都是统一的:Graphic中的Rebuild。
Rebuild实际上是实现了ICanvasElement的接口。
当顶点数据遭到修改,则立刻会进行重建,实际上我们只需要关心何时SetVerticesDirty会被修改就可以明白到底什么时候UI会进行重建。
不过在此之前,我们先看看Rebuild在何时会被调用。
查找引用,发现是在CanvasUpdateRegistry中的PerformUpdate中进行调用,这个PerformUpdate是挂载在Canvas.willRenderCanvases这个事件上的,也就是Canvas进行渲染之前便会进行调用。
SetVerticesDirty被调用的情况
我们回过头来看一看何处会修改SetVerticesDirty:
当调用了SetVerticesDirty则会修改该值并且添加到Canvas的重建列表当中。
调用该函数的地方就不少了,查找之后总共34处调用。
文字的以下属性进行变化都会进行Mesh重建,所以如果有大量需要经常变动的Text就要小心了。
同样的图片也有属性一旦改变就会引发改变,其中我们比较常用的可能就是尺寸了,例如CD、位置修改等等,都会造成重建。
RawImage中进行修改的时候也会产生重建
当蒙版进行修改的时候也会进行重建
采用Shadow的时候也会产生重建
了解到何处会对Mesh重建进行调用我们就可以尽可能减少重建消耗。
总结
例如弹幕或者是MMO中飘血文字都可以通过自己重写OnPopulateMesh来自己对Mesh进行合批而不需要通过Canvas对所有元素进行逐一的合批,提高效率。
UGUI源码之绘制初探相关推荐
- [UGUI源码剖析]—Rebuild 网格重建(画布刷新)系统
几个比较重要的类和接口: Canvas.CanvasUpdateRegistry.ClipperRegistry.LayoutRebuilder.LayoutGroup.Graphics.Maskab ...
- Unity中的UGUI源码解析之事件系统(6)-RayCaster(下)
Unity中的UGUI源码解析之事件系统(6)-RayCaster(下) 接上一篇文章, 继续介绍投射器. GraphicRaycaster GraphicRaycaster继承于BaseRaycas ...
- ugui源码_UGUI 源码笔记(一)文件结构和部分组件使用
这是我阅读 UGUI 源码记录的相关笔记,共三部分.文件结构和部分组件使用.输入事件.核心部分 ZeroyiQ:UGUI 源码笔记(一)文件结构和部分组件使用 ZeroyiQ:UGUI 源码笔记(二) ...
- UGUI源码分析:开关组件Toggle与ToggleGroup
系列 UGUI源码分析系列总览 相关前置: UGUI EventSystem源码分析 UGUI源码分析:Selectable交互组件的基类 文章目录 系列 Toggle Toggle组件属性介绍 初始 ...
- UGUI源码分析:GridLayoutGroup网格布局组件与ContentSizeFitter尺寸调节组件
系列 UGUI源码分析系列总览 相关前置: UGUI CanvasUpdateSystem源码分析 UGUI源码分析:LayoutSystem布局系统 UGUI源码分析:LayoutGroup中的纵横 ...
- UGUI源码分析:LayoutGroup中的纵横布局组件(HorizontalOrVerticalLayoutGroup)
系列 UGUI源码分析系列总览 相关前置: UGUI CanvasUpdateSystem源码分析 UGUI源码分析:LayoutSystem布局系统 文章目录 系列 UML图一览 LayoutGro ...
- 如何把UGUI当做一个插件使用(删除Unity中的UGUI,导入UGUI源码进入项目)
最近闲着没事,一直也都知道UGUI是开源的,所以就想着把UGUI的源代码放到Unity里面,看一看能不能用,经过一番调试,终于弄好了,有兴趣的同学可以看一下,欢迎交流沟通. 欲练神功,必先自宫.第一步 ...
- Unity中的UGUI源码解析之事件系统(2)-EventSystem组件
Unity中的UGUI源码解析之事件系统(2)-EventSystem组件 今天介绍我们的第一个主角: EventSystem. EventSystem在整个事件系统中处于中心, 相当于事件系统的管理 ...
- Unity中的UGUI源码解析之事件系统(8)-输入模块(中)
Unity中的UGUI源码解析之事件系统(8)-输入模块(中) 接上一篇文章, 继续介绍输入模块. Unity中主要处理的是指针事件, 也就是在2d平面上跟踪指针设备输入坐标的的事件, 这一类事件有鼠 ...
- Unity中的UGUI源码解析之事件系统(9)-输入模块(下)
Unity中的UGUI源码解析之事件系统(9)-输入模块(下) 接上一篇文章, 继续介绍输入模块. StandaloneInputModule类是上一篇文章介绍的抽象类PointerInputModu ...
最新文章
- arm优化编译参数选项解释
- 关于高德地图Android开发时地图只显示一次、第二次打开不定位的解决办法
- UTF-8带BOM和不带BOM的转换
- python自定义变量名标识符,【python】3 标识符和关键字
- boot定时任务开启和关闭 spring_Spring-Boot 下定时任务通过配置文件控制开关和执行时间...
- 使用Android自带DownloadManager下载文件
- STM32的JTAG下载模式
- Qt4项目迁移到Qt5问题:greaterThan(QT_MAJOR_VERSION, 4): QT += widgets .
- Ansible@一个高效的配置管理工具--Ansible configure management--翻译(十一)
- 函数解素数求距离问题
- java断点上传分片保存方案_分片上传与断点续传解决方案
- 备考分享!第十一届CDA考试Level Ⅱ 优秀考生采访
- 项目管理笔记-第十章 项目沟通管理
- cpu过剩是什么意思_现在为什么人们都说CPU性能过剩,而不说显卡性能过剩?
- 19-Python基础知识学习-----迭代器与生成器
- Unity项目-黑魂复刻(四)玩家控制器(翻滚以及跳跃操作改动)
- win10/11下wsl2安装gpu版的pytorch(避坑指南)
- 编程实现顺序表的基本操作
- sql server 学习教程
- typora笔记使用base64编码图片
热门文章
- Rust: flat_map、filter_map、for_each
- Linux宝库名人轶事栏目 | 笨叔与Linux的那些事(下)
- 【优化算法】白鲨优化算法(WSO)【含Matlab源码 623期】
- 毕设题目:Matlab通信
- 【光学】基于matlab GUI带切趾的光线布拉格光栅滤波特性仿真【含Matlab源码 1505期】
- 【三维路径规划】基于matlab遗传算法无人机三维路径规划【含Matlab源码 1268期】
- 【旅行商问题】基于matlab免疫算法求解旅行商问题【含Matlab源码 195期】
- 中国ai人工智能发展太快_新的AI计算遥远行星的速度快100,000倍
- 星球大战telnet_重制星球大战:第四集(1977)
- mysql存储food_Mysql存储过程