背景

身为一名游戏开发从业者在过去10多年时间里在很多项目中积累了经验同时身在大厂希望分享自己,为此我在2021年出版了一本书《Unity3D高级编程:主程手记》书中记录了我过去做过的所有项目各个模块的技术要点包括框架、算法、原理和优化方案等等(博客:luzexi.com)。同时在这些经历中我深刻认识到引擎作为游戏研发的基础建设和底层逻辑它的重要性。多年下来对引擎也有了自己的认知,最近几年通过对引擎的解剖和分析深入了解了它的模块结构、流程、技术原理和实现方式。

最近萌生了一个想法希望能系统性的对游戏引擎进行解剖,于是开始了这个行动。我想通过这个解析过程更深入地理解引擎并分享它们,因此我决定写一系列文章来剖析引擎的各个模块,包括结构、流程、技术原理和实现方式,为了能与工作相结合从而发挥最大效益在解析引擎的同时也把这些技巧都运行在了自己的项目中。

由于过去几年中一直努力致力于性能优化相关工作,包括性能工具研发、性能分析、性能优化和框架结构改造升级等,在工作中也深刻明白理解引擎中的架构和引擎各模块的运作原理对于优化工作的重要性。实际上“引擎技术”已经不单单是引擎了为了方便描述我这里统称为“引擎技术”,同时为了能更好的理解引擎在这个系列的文章中将用图的方式来描述原理、流程和结构,以加速自己和读者们理解知识。

我将这个系列命名为《图解游戏引擎》,希望通过‘图’的方式形象化解读抽象化的技术。写作过程也是我的学习过程,通过学习和回顾更深入地理解引擎的技术。这个系列的文章将系统性的解析游戏引擎技术,涵盖包括引擎的历史、架构、功能框架、模块结构、执行流程、策略机制、公式算法、硬件结构等,另外时间允许的话也会通过些自制引擎的案例来完成引擎技术实战。

目录

一、UGUI结构概述

二、UGUI图集与组件

2.1 图集算法

2.2 组件原理

三、UGUI输入机制

四、UGUI内核

4.1 UGUI Core结构

4.2 合批算法与流程

4.2.1 元素排序

4.2.2 元素合批计算

4.2.3 合批网格生成

五、UGUI渲染与材质

5.1 渲染流程

5.2 渲染材质

摘要:UGUI是Unity引擎中的内置UI框架,用于创建2D和3D用户界面。它的框架特点包括UI分层、矩形变换、事件系统、内置UI组件、UI图集、材质与效果、UI画布和UI合批。UGUI框架由三部分构成,图集工具、业务组件和合批渲染,分别负责打包图集、具体效果的实现和合批渲染。

关键词:Unity引擎、UI底层框架、UGUI解析

内容

一、UGUI结构概述

在Unity引擎中UGUI(Unity UI,又称为Unity GUI)是一种内置的UI框架,用于创建2D和3D用户界面。UGUI框架提供了一系列的UI组件和工具以便开发者能够快速轻松地构建用户界面。

组件库的作用_组件库在哪_游戏组件运行库

(UGUI的框架特点)

它的框架特点如下:

1)UI分层:UGUI采用层次结构组织UI元素,这使得元素的管理和定位变得更加简单。在场景层级中每个UI元素都是作为游戏对象(GameObject)呈现并附加了特定的UI组件。

2)矩形变换(RectTransform):UGUI引入了矩形变换替代了传统的Transform组件以支持2D布局。RectTransform组件定义了UI元素的大小、位置和锚点,并支持常见的布局元素如锚点、边距和偏移等。

3)事件系统(EventSystem):UGUI提供了一个事件系统用于处理和传输用户交互事件,如点击、拖动、滚动等。这个系统允许开发者方便地为UI元素编写响应事件的代码实现与用户的交互。

4)UI组件:UGUI有许多内置的UI组件,如按钮(Button)、文本(Text)、图片(Image)、滑动条(Slider)、滚动视图(Scroll View)等,这些组件提供了基本的UI功能使开发者可以轻松地构建用户界面。

5)UI图集:UGUI图集会在打包时自动生成使得元素共享相同的纹理和材质,这样可提高渲染效率并降低绘制调用次数优化了UI元素的合批渲染。

6)材质与效果:UGUI内置了一些材质与效果,开发者可以更快的尝试UI上的效果,例如描边、文字阴影等。

7)UI画布(Canvas):UGUI用画布来渲染整个UI,它负责确定像素大小并处理可视化层级关系。开发者可以将画布分为屏幕空间(Screen Space)、世界空间(World Space)两种模式来实现3D场景与UI之间的交互。

8)UI合批(UI Ratching):UGUI使用了一种渲染优化技术即UI合批(UI Batching),将绘制相同材质的UI元素合并成一个绘制调用以提高渲染性能。

组件库在哪_组件库的作用_游戏组件运行库

(UGUI的示例展示图)

UGUI框架由三部分构成,图集工具、业务组件、合批渲染,三部分内容可以概括为:编译打包时工具生成图集、业务组件在渲染时构建顶点、Canvas做合批并提交渲染。

组件库在哪_组件库的作用_游戏组件运行库

(UGUI的三个部分图)

如上图,三部分内容的具体职责范围:

1、工具的部分,负责打包图集以及可视化的编辑

2、C#的组件部分,负责具体效果的实现,主要是顶点、颜色、uv的逻辑组合,除了网格还包括额外效果实现。

3、C++的Core部分,主要是Canvas提供的功能,它提供了合批算法和渲染管线,因此负责网格渲染、合批、引擎渲染调用。

二、UGUI图集与组件

2.1 图集算法

我们知道UGUI的图集在编辑器启动或打包时进行,通过tag来识别哪些图被打进同一个图集内,同时现在版本中如果是ETC1的话会拆分alpha单独打个图集,如果一个图集不够容纳所有tag的图片就会新增一个图集来容纳剩余的图。

组件库的作用_组件库在哪_游戏组件运行库

(图集示例图)

前面文章中我们知道了Unreal的图集算法是通过上下左右空间分割的方式把图集的空余空间以树状的方式存储起来,这样就实时生成图集时就更方便了。但Unity的UGUI的图集的生成由于是打包或编辑器启动时生成的,它实际上一个一次性生成过程,因此它的图集生成方式可以不用这么麻烦简单点,在生成图集前把所有的图都模拟放入图集,放入时按从上到下从左到右的方式依次遍历填入模拟的图集中,等所有图都填入的模拟图集中后,我们就得到了所有图在图集中的位置和图集的大小以及UV的信息,接着才真正的生成图集,把前面的信息都填入空白的图集中。

UGUI依赖Unity的自动图集生成工具:Sprite Packer或Sprite Atlas,在Unity中这些工具负责将多个小纹理打包到一张大纹理上。

Sprite Atlas使用类似于矩形装箱(Rectangle Packing)算法的方法来生成图集,其中贪婪方式的最大矩形算法(Maximal Rectangles Algorithm, MaxRects)是该领域知名的图集生成算法之一,这种算法在保证装箱速度的同时,尽量减少大图的尺寸,从而获得较高的空间利用率。

MaxRects算法的基本步骤如下:

1)首先获取所有填充贴图元素的尺寸,对其进行升序或降序排序。这样可以确保算法从大到小或从小到大迭代纹理。

2)初始化一个容器来存放当前运算的图集,容器具有一定的最大宽度和最大高度值,可按需要进行设置。

3)从排序后的贴图元素列表中依次插入每个纹理,插入时遍历所在图集像素中放得下的位置,选择合适的插入位置。合适的插入位置将使装箱效果最优(如:最低浪费、最佳边界对齐等)。

4)将选定的贴图元素插入到找到的位置,并更新图集中的可用空间,循环直到所有贴图元素填充完成。

5)批量处理结束后,图集被安排满所有输入贴图元素完成了图集的生成。

组件库在哪_组件库的作用_游戏组件运行库

(图集生成示例图)

Unity中的Sprite Packer或Sprite Atlas使用的算法稍稍不同于MaxRects,UGUI会先模拟计算填充位置待计算完毕后再处理填充,同时在UGUI中的图集查看器上可以选择不同的装箱策略,方便开发者根据开发功能选择图集的生成策略。

2.2 组件原理

组件库的作用_组件库在哪_游戏组件运行库

(组件模块与Core模块概念图)

C#组件部分分为三块,组件、布局、效果。组件主要负责运行时的图像网格生成,布局主要负责把方便地为UI元素提供自动布局功能,效果主要为UI元素提供一些额外的渲染效果。

元素组件:

元素组件以Graphic为基类进行扩展,Graphic负责UI元素的网格构建,它搭建了一些基础的逻辑处理供组件类使用,例如当UI组件的状态改变时Graphic会做Dirty脏标记以便在Canvas做Rebuild重构计算前知道哪些元素需要重构。

关于Graphic的Dirty脏标记实现里面有三种类型,即布局Dirty标记、顶点Dirty标记、材质Dirty标记,无论布局、顶点、材质、颜色发生改变了,都会进行标记以便识别是否需要重构。

基于Graphic的派生类很多,都是UGUI的内置元素,比如Image、RawImage、MaskableGraphic、Text等等,都是我们在开发中常用的元素。

组件库在哪_游戏组件运行库_组件库的作用

(元素组件脏标记流程图)

Mask组件:

Mask、RectMask2D遮罩组件与其他普通组件不同,它是独立于Graphic存在的,它本身不绘制任何图形专门用于裁剪它下面子节点的图形,因此它们的存在比较特殊。

同时Mask和RectMask2D也有些区别,它们的裁剪方式不同,RectMask2D使用顶点裁剪方式,即当UI元素生成顶点后对超出范围内的顶点做二次裁剪,而Mask则使用材质球Shader裁剪方式,即在GPU执行顶点函数时做Cut顶点裁剪。

Modifier组件:

效果类被称为Modifier(修改者),都需要继承IMeshModifier、IVertexModifier或IMaterialModifier来统一调用并修改元素。

Modifier效果包括外边缘、UV动画、UI阴影等,同时客户端程序员可自行扩展Modifier组件来增强效果。

Layout组件和Culling组件:

Layout局部组件用于UI上元素的自动布局,可惜它的效率不高,原因是它过于注重模块化封装,导致封装逻辑中的冗余较多。

Culling裁剪组件专门用于RectMask2D框外的顶点裁剪。

其他扩展类和工具组件

VertexHelper用于网格数据缓存和填充

ObjectPool用于对象池申请和释放

三、UGUI输入机制

引擎的输入机制都有相似之处,例如大多使用轮询的方式(除了Windows需特殊处理)来获取输入数据,接着对输入数据的不同类型例如鼠标、触屏、键盘做识别处理作为输入信号传输给业务逻辑。

UGUI输入机制也遵循一般引擎的做法,在引擎轮询中加入输入轮询来检查输入设备的操作,再通过InputModule过滤成输入信号传递给业务逻辑模块,Unity与Unreal的区别在输入后的碰撞测试算法不同。

UGUI的输入机制可以简要概述为:

1)用户与屏幕互动(例如点击、拖动等)。

2)输入Input轮询获取输入设备数据。

2)根据平台和输入设备,InputModule处理这些输入事件并转换为相应的UGUI事件。

3)EventSystem将这些输入事件做碰撞测试获取正确UI碰撞元素并发送。

4)接受到事件的UI元素基于这些事件触发回调函数或执行内置行为。

输入机制主要依赖以下几个关键组件:

1)EventSystem:EventSystem组件是UGUI中负责输入处理的主要组件。它用于将输入事件(例如触摸、点击、拖动等)分配给UI元素,因此场景中只需要有一个EventSystem实例来处理所有的输入事件。

2)InputModule:InputModule是EventSystem的子组件,负责将特定类型的输入设备的输入数据转换为UGUI可以理解的事件。InputModule的示例包括:StandaloneInputModule(用于独立平台,如PC和游戏机)、TouchInputModule(用于触摸屏设备)和CustomInputModule(用于自定义输入处理)。InputModule会根据具体平台和输入类型来处理输入事件。

3)GraphicRaycaster:GraphicRaycaster是负责碰撞测试的组件,由于UGUI使用碰撞测试的方式来计算响应元素,因此输入的点击和释放都需要GraphicRaycaster通过射线碰撞和矩形点位计算来判断最终响应的UI元素。

4)UI元素(例如Button、Slider、Toggle等):这些元素是UGUI系统的基础组件,它们具有与用户交互的能力,并且可以通过EventTrigger组件或内置API(例如onClick、onValueChanged等)来响应输入事件。当InputModule检测到某个输入事件时,它会将这个事件发送到具有IPointerClickHandler、IPointerEnterHandler等接口的UI元素,这些元素会处理这些事件并调用相关的回调函数。

5)事件触发器(EventTrigger):它允许在特定的游戏对象上附加开发者自定义的事件监听器。例如,当用户点击Button时,EventTrigger会触发指定的回调函数。这使得开发者可以处理更复杂的UI交互,例如自定义动画、声音或控制器事件等。

与Unreal不同的是Unity的UGUI并没有使用Grid栅格化存储,而是直接使用碰撞测试和图形点位计算的方式计算碰撞响应元素,步骤为:

1)先从Canvas中取出所有需要响应点击的UI元素

2)对这些元素依次做碰撞测试,碰撞测试分两种,一种是点在矩形内的计算判断,另一种是用Ray射线做碰撞测试

3)对所有有碰撞的UI元素按depth排序,排序第一位的UI元素做响应,即把输入事件信息传递过去。

4)不同类型的UI元素根据自己元素组件的逻辑功能做出自己的响应动作。

组件库在哪_游戏组件运行库_组件库的作用

(输入模块关系图)

UGUI的点击、拖动、文字输入,最终都是源自引擎的输入组件Input。引擎的Input组件本身就提供了,鼠标点击事件、手指点击事件、点击次数、拖动位移细节等数据。EventSystem是UGUI的输入模块的起点也是管理类。它负责获取输入信号,并将输入信号分派给响应的模块做处理,因此它是所有输入逻辑处理的中转站也是输入源的管理者。UGUI对Input的输入数据和接口进行了封装,把它封装成标准输入模块和触屏输入模块。EventSystem在每帧Update时都会去更新当前的输入模块,从而获取输入信号并做出响应。

在输入信号获取之后会进行点击位置与元素的碰撞测试,即GraphicRaycaster组件的工作。输入点的碰撞测试分为了两种,一种是2D的点是否在元素矩形范围内的方式来判断点与UI元素的碰撞,另一种是直接用Ray射线的物理碰撞测试。非3DUI的碰撞测试以第一种为主,即2D的点是否在元素范围内,同时每个继承Graphic的可碰撞元素都需要重写Raycast函数,该函数用来判断输入点是否当前UI元素范围内。

限时特惠:本站每日持续更新海量各大内部网赚创业教程,会员可以下载全站资源点击查看详情
站长微信:11082411

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。