文章目录
- 前言
- 一、UGUI运行原理
- 二、UGUI组件分析
- 1、Canvas组件
- 2、Canvas Scaler
- 3、Graphic Raycaster组件
- 4、EventTrigger组件
- 5、Image 和 RawImage组件
- 5、Mask 和 RectMask2D组件
- 6、Button、Sider、DropDown等组件
- 三、UGUI源码分析
- 三、UGUI优化
- 四、UI框架设计
- 五、UI适配
- 总结
前言
理论
一、UGUI运行原理
在工作中我在制作一个类似聊天框的功能,里面的信息会随输入不断增加,在增加到一定数量时会直接报错,mesh顶点数超过xx,为什么呢?
Mesh.vertices is too large. A mesh may not have more than 65000 vertices
如果是一个模型,我们可以使用偏移等操作实现超过65000的顶点数。而作为一个UI,为什么和mesh顶点数有关呢?UGUI是由3D网格建立起来的UI系统,UI的创建其实是网格的构建。一个按钮相当于一个模型,她有她的网格,网格再绑一个材质。这样设计就会有一个问题,一个图片对应一个模型,如果有成千上万的模型一起渲染,drawcall压力会很大,性能大幅下降甚至造成严重卡顿。
UGUI提供了解决方法,将相同的图片进行合成,合成出一张图。这样多个图片拥有一个相同的着色器的材质球,并把零散的网格合成几个大型网格进行渲染;多张图片存在于一个图集之中,图片和材质球只需要改变模型顶点的UV和颜色,根本不需要重复绘制;当然,是按照不同层级的拥有相同材质球参数的进行合并,最终,UI系统的效率大幅提升。
不过,在UGUI组件移动、删除时会拆分并合并网格,如果这类操作很多很平凡 ,会消耗大量性能,所以在实际项目中最好减少此类操作。
那么其合并规则是什么呢,仅在同一个Canvas的相同层级的拥有相同材质的元素进行合并。同一层级指的是在Hierarchy视图中,共享同一个直接父对象的UI元素。这些元素被称为“兄弟”元素。在同一层级的UI元素满足合并条件时,Unity会将这些元素合并为一个批次进行渲染,从而减少绘制调用。
例如,考虑以下Hierarchy结构:
Canvas
├── Panel (Image)
│ ├── Button1 (Button)
│ ├── Button2 (Button)
│ └── Button3 (Button)
└── Background (Image)
在这个结构中:
- Button1、Button2和Button3处于同一层级,因为它们共享相同的父对象Panel。
- Panel和Background不处于同一层级,因为它们是Canvas的直接子对象。
- 为了优化渲染性能,应该尽量确保同一层级的UI元素使用相同的材质和渲染属性,这样Unity可以将它们合并为一个绘制调用。
- 在计算时会先计算元素重叠的关系,再一级一级的进行同一层级的合并。
二、UGUI组件分析
1、Canvas组件
Canvas的渲染模式有三种:
- Overlay模式:将UI渲染至屏幕顶层。所有的UI元素都会按照屏幕空间的坐标进行绘制,UI的位置和大小不受相机的影响。一般情况下性能最好。
- Camera模式将Canvas渲染在指定的相机前,UI元素的绘制位置和大小依赖于相机的设置。UI元素在渲染时会根据相机的视图和投影进行计算。通常在UI中带有大量特效或者HUD(Heads-Up Display)和透视UI效果的UI时使用。
- World Space模式适用于需要在3D场景中显示和交互的UI需求,如游戏中的3D界面、物体标记和交互面板。
2、Canvas Scaler
屏幕适配组件有三种缩放模式:
- Constant Pixel Size(常量像素大小):在这种模式下,UI元素的大小以像素为单位保持不变。这意味着在不同分辨率下,UI元素的实际大小会有所变化,但像素的数量保持不变。这种模式适用于游戏中需要保持像素风格的UI设计。
- Scale with Screen Size(随屏幕尺寸缩放):在这种模式下,UI元素的大小会随着屏幕的尺寸进行缩放。可以设置参考分辨率,UI元素会根据当前屏幕分辨率与参考分辨率的比例进行缩放。这种模式适用于需要在不同分辨率下保持相对大小和比例的UI设计。
- Constant Physical Size(常量物理大小):在这种模式下,UI元素的大小以物理尺寸(如英寸)为单位保持不变。这意味着在不同分辨率下,UI元素的像素数量会根据屏幕的分辨率进行调整,以保持物理尺寸不变。这种模式适用于需要在不同设备上保持相同物理尺寸的UI设计,比如移动设备上的虚拟按钮大小。
3、Graphic Raycaster组件
Unity UI系统中用于处理射线交互的组件之一。它允许鼠标点击或触摸事件与Canvas上的UI元素进行交互。Graphic Raycaster主要用于检测射线与UI元素的碰撞,从而触发相应的事件,如点击、拖拽等。
4、EventTrigger组件
输入事件触发器,与此脚本绑定的UI物体都可以接收输入事件。比如按下、弹起、点击、开始拖曳、拖曳中、结束拖曳、鼠标滚动等事件。它主要起到点击响应作用,配合前面的Graphic Raycaster进行响应。
5、Image 和 RawImage组件
mage 组件用于显示 Sprite 图像,通常用于显示 UI 图标、按钮、背景等。如果不进行交互操作可以将Image上的RaycastTarget属性设置为false,可节省性能。也可以参考拓展编辑器重写Image。
RawImage 组件用于直接显示 Texture2D 或 RenderTexture,通常用于显示动态图像、视频帧、相机渲染等。
5、Mask 和 RectMask2D组件
用于控制 UI 元素显示范围的组件,
Mask:
Mask 组件用于限制子对象在其范围内可见的区域,通常用于创建滚动列表、镂空效果等。
可以将 Mask 组件放置在父对象上,并将需要裁剪的子对象放置在 Mask 内部。
Mask 使用 Unity 自身的裁剪功能实现,因此在性能方面可能略逊于 RectMask2D,特别是当 UI 元素较多时。
RectMask2D:
RectMask2D 组件也用于限制子对象的显示范围,但它的实现方式更加高效。
RectMask2D 通过将一个矩形遮罩应用于子对象,以实现裁剪效果。
可以将 RectMask2D 组件直接添加到需要裁剪的 UI 元素上,而不需要额外的父对象。
简单的讲两者的区别就是,Mask使用顶点重构的方式剔除矩形区域外的部分,而RectMask2D 则是使用着色器的方式来剔除,效率上自然是后者更高。
6、Button、Sider、DropDown等组件
作为unity开发经常使用的组件,使用方法很简单,就不赘述了。值得注意的一点是,我们在项目经常使用某些组件且发现其对项目的有所欠缺时,请毫不犹豫的思考并重写她们,做定制化需求,如双击按钮、长按按钮等,重写难度之低就在于你是否舍得花10分钟百度一下或者使用下chatgpt。
三、UGUI源码分析
除了Canvas和RectTransform这两个非常核心的类以外,UGUI的源码都是开源的。内容也没有多高深,其中设计到网格的构建、重构、裁剪等操作,她在关心怎么减少重构,提高内存以及GPU的使用、减少性能消耗。
学习源码的方法是找到项目的包将其拷贝至unity项目中,再移除包管理器的UI包。
UGUI事件模块分析:待写
UGUI核心代码分析:待写
三、UGUI优化
UGUI优化其实属于一个很杂的东西,像对象池、打图集、字体拆分、无限循环、GC优化等,有不少涉及其她的知识点。某些优化方式如果在“宽松”的条件下不会被使用,通常在项目后期进行,比如多机型的适配等。
UGUI优化:待写
四、UI框架设计
UI框架通常是根据项目需求进行定制,但其核心原理基本不变。掌握UI框架的设计方法,实际上就超越了初级程序员的水平。本章将讨论UI框架的原理设计部分,具体实现可以通过B站、YouTube等视频平台免费学习,通常课程时长为几十个小时。
本人当初(数年前)也是在B站一步步实现自己的第一个UI框架的。回顾这段经历,许多曾经不明白的问题逐渐变得清晰,框架中用到的巧妙设计也越来越能理解。如今,很少有人会在项目中从头到尾重新实现一个UI框架,通常是基于已有的技术积累或使用现成的、经过测试的框架。在掌握原理后,推荐大家使用像QFramework这类成熟的框架,再根据项目需求进行修改和优化。QFramework的作者凉鞋也提供了相关的收费课程,有兴趣的可以了解一下。当然,本人已经是个老油条,没有再次学习的打算。
UI框架设计:待写
五、UI适配
这个我是不知道该不该写出来,她涉及到UI的锚点计算这方面。又得力于Android和Ios百花齐放的屏幕以及刘海,从而诞生了这一项工作,我也是有幸做过相关工作,对苹果的刘海深恶痛绝。当然,在做的过程中找到了一些辅助办法轻松的解决了问题。
UI适配:待写
总结
本篇文章分析了UGUI系统的基本构成和几个UGUI的学习重点,内容将逐步完善。希望大家多多点赞支持,如果有写得不好的地方或者不理解的部分,可以留言或私信,我将一一解答或改正。
有五个待写,就是五篇独立的文章,后续会补充上链接。等不及的朋友可以先看看其他博主的文章,我们一起共同进步,持续学习。