《Unity3D高级编程之进阶主程》第四章,UI(二) - UGUI的原理及组件使用详解

前文对NGUI和UGUI进行了比较,讲述了如何选择UI系统作为项目的UI框架。这篇我们来讲讲,UGUI的原理,以及UGUI的组件使用详解。

===

UGUI的初级和高级使用详解

首先我们来介绍下ugui的运作机制。

UGUI是在3D网格下建立起来的UI系统,它的每个可显示的元素都是以3D模型网格的形式来构建起来的。当UI被实例化时,UGUI首先要做的事就是构建网格。

也就是说当Unity3D制作一个图元,或者一个按钮,或者一个背景时,都会先构建一个方形网格,再将图片放入网格中。可以理解为制造了一个3D模型,用一个网格绑定一个材质球,材质球里存放了要显示的图片。

如果每个元素都会生成一个模型并且绑定一个材质球存入一张图片的话,UI上成千上百个元素就会拥有成千上百个材质球,以及成千上百张图。这样使得引擎在渲染时都需要读取成千上百张图,对每个材质球和网格都进行渲染,这会导致性能开销巨大,drawcall过高,可以简单的理解为一个材质球一个drawcall。(drawcall的原理我们将在后面的章节中介绍)

UGUI当然做了优化,它将一部分相同类型的图片都集合起来合成一个张图,然后将拥有相同图片相同shader的材质球合并成一个材质球,并且把分散开的模型网格也一起合并了,这样就生成了几个大网格和几个材质球,以及少许整张的图集。节省了很多材质球,图片,网格的渲染,UI的效率更高了很多,游戏在进行时才会顺畅。这就是我们常常在UI制作中提到的图集的概念,它把很多张图片都放置在一张图集上,导致大量的图片和材质球不需要重复的绘制,只要改变模型顶点上的uv即可。

UGUI也并不是所有的网格和材质球都合并成一个,只有把相同层级的元素,以及相同层级上的拥有相同的材质球参数的才合并在一起。合并成一个网格了就是一个静止的模型了,如果我们移动了任何元素,或者销毁了任何元素,或者改变了任何元素的材质球参数,UGUI则会销毁这个网格,重新构建一个新的。我们设想下,如果我们每时每刻都在移动一个元素的话,UGUI就会不停的拆分合并拆分合并,就会不停的消耗CPU,来使得画面保持应该有的样子。

因为这些合并和拆分的操作会消耗很多CPU,UI系统要做的就是尽一切可能节省些CPU消耗,把尽量多的剩余CPU让给项目逻辑。UGUI在制作完成成品后性能优劣差距很多时候都会出现在这里,合并的最多的元素,拆分次数最少的UI,才能达到优秀的性能开销.

下面我们来主要介绍下UGUI的核心组件。

核心组件Canvas

	Canvas,我们暂且叫它画布。Canvas就相当于画画时铺在上边的画板,我们把各类元素放在画布上后,Canvas要做的事情就是合并这些元素。

	合并的规则为,同一个Canvas里,相同层级的,相同材质球的元素进行合并,从而减少Drawcall。不过相同层级的概念并不是gameobject 上的节点层级,而是覆盖层级。Canvas说如果两个元素重叠,则可以认为它们是上下层关系,把所有重叠的层级数计算完毕后,第0层的所有元素统一合并,第1层的元素也统一合并,以此类推。

	Canvas上的参数 Render Mode 渲染模式比较重要,这里详细介绍下,你可以选择不以Camera为基准的Overlay模式,也可以选择Camera为基准的Screen Camera模式,也可以选择3D世界为基准的World Space模式。三者适合于三种不同的的使用场景各有不同。

	Overlay模式并不与空间上排序有任何关系,空间上的前后位置不再对元素起作用,它常用在纯UI的区域内,这种模式下Camera排序有别与其他模式,Sort order参数在排序时被着重使用到,Sort order参数的值越大,越靠前渲染。在这个模式下没有Camera的渲染机制因此很难加入普通的3D模型物体来增加效果。

	Screen Camera模式,相对比较通用一点,它依赖于Camera的平面透视,渲染时的布局依赖于它绑定的Camera。想让更多的非UGUI元素加入到UI中,Screen Camera模式更加具有优势。这种模式是实际项目中制作UI最常用的模式,不过UGUI底层有对排序做些规则,如对元素的z轴不为0的元素,会单独提取出来渲染,不参与合并。

	World Space模式,主要用于当UI物体放在3D世界中时用的,比如,一个大的场景中,需要将一张标志图放在一个石块头上,这时就需要World Space模式。它与 Screen Camera 的区别是,它常在世界空间中与普通3D物体一同展示,依赖于截锥体透视(Perspective)Camera。它的原理挺简单的,与普通物体一样当UI物体在这个Camera视野中时,就相当于渲染了一个普通的3D面片,只不过除了普通的渲染Canvas还对这些场景里的UI进行合并处理。
Canvas Scaler
	这是个缩放比例组件,用来指定画布中元素的比例大小。

	有简单指定比例大小的Constant Pixel Size模式,也有Scale With Screen Size以屏幕为基准的自动适配比例大小,或者Constant Physical Size以物理大小为基准的适配规则。

	在实际手游项目里,设备的屏幕分辨率变化比较大,通常使用以屏幕为基准的自动适配比例大小的Scale With Screen Size选项。
Graphic Raycaster
	输入系统的图形碰撞测试组件,它并不会检测Canvas以外的内容,检测的都是画布下的元素。当图元素上存在有效的碰撞体时,Graphic Raycaster 组件会统一使用射线碰撞测试来检测碰撞的元素。

	我们也可以设置完全忽略输入的方式来彻底取消点击响应,也可以指定阻止对某些layers进行相应。
EventTrigger
	输入事件触发器,与此脚本绑定的UI物体,都可以接受到输入事件。

	比如(鼠标,手指)按下,弹起,点击,开始拖动,拖动中,结束拖动,鼠标滚动事件等。

	它主要是起到点击响应作用,配合前面的 Graphic Raycaster 响应给输入事件系统。
Image,RawImage
	这两个是UI里的主要部件,它们可以对图片进行展示,包括图片,图集。

	两者的区别是Image仅能展示图集中的图元但可以参与合并,而RawImage能展示单张图片但无法合并。通常我们会将小块的图片,打成图集来展示,这样更节省性能也更节省内存,这也是UGUI自动集成的功能,每个图片资源都有一个tag 标记,标记决定了哪些元素会合并到同一张图集内,如果没有tag标记,则默认不会合并图集它自己就是自己的图集。

	不使用图集而使用RawImage展示单张图片的时,通常都是由于图片尺寸太大导致合并图集效率太低,或者相同类型的图片数量太多,导致合并图集后的图集太大,而实际在画面上需要展示的这种类型的图片又很少,图集方式反而浪费大量内存空间,则使用RawImage逐一展示即可。
Mask,RectMask2D
	遮挡组件,可以将其子节点下矩形区域外的内容剔除,是滚动窗口中最常用的组件。

	这两种方式的主要是在剔除的方法上有所区别,在实现效果上都是一样的,其中Mask 使用顶点重构的方式剔除矩形区域外的部分,而 RectMask2D 则采用 Shader 的剔除方式,每个元素都有自己的材质球实例和实例参数。

	Mask 和 RectMask2D 它俩具体的剔除算法和源代码分析我们将在后面的UGUI源码剖析章节讲解。
其他组件
	其他大部分逻辑组件都是可以重写的,比如按钮组件Button,切换组件Toggle,滚动条组件ScrollBar,滑动组件Slider,下拉框组件DropDown,视图组件ScrollView,如果不想使用它们,觉得它们的功能不够用,我们是可以用Image,Mask等几个核心组件组合后重写的。

	在实际工作中,很多项目都会自定义属于自己的组件,为什么要自定义呢?很多时候项目里的需求更多样化,有自己的组件可以在特殊需求和特殊逻辑时,能够好不费劲的更改自定义的组件。所以大部分项目中,都会重写一些组件来用来给自己项目使用,也有一些人总结了这些组件的经验,写了些比较好用的组件开源在Github上。
· 书籍著作, Unity3D, 前端技术

感谢您的耐心阅读

Thanks for your reading

  • 版权申明

    本文为博主原创文章,未经允许不得转载:

    《Unity3D高级编程之进阶主程》第四章,UI(二) - UGUI的原理及组件使用详解

    Copyright attention

    Please don't reprint without authorize.

  • 微信公众号,文章同步推送,致力于分享一个资深程序员在北上广深拼搏中对世界的理解

    QQ交流群: 777859752 (高级程序书友会)