原文地址:https://github.com/jareguo/unity-ugui/tree/master/overview
Unity终于在即将到来的4.6版本内集成了所见即所得的UI解决方案(视频)。事实上从近几个版本开始,Unity就在为这套系统做技术扩展,以保证最终能实现较理想的UI系统。本文试图通过初步的介绍和试用,让读者对这套系统有大体的了解,以便更进一步评估这套UI系统好不好用,适合用在什么项目。为了避免坑挖太深,更进一步的试用和评估我将在《用uGUI开发自定义Toggle Slider控件》中进行论述。为论述方便,下文将这套New UI System简称为uGUI,并且以X-UI指代现有第三方UI插件。
(测试只针对Unity 4.6.0 beta 10,正式版可能会有所出入。目前Unity没提供文档,本人半桶水,欢迎群众在微博或Issues里吐槽!)
Rect Transform继承自Transform,是uGUI相比X-UI最显著的区别[注1]。当你为Empty GameObject加入一个UI Component时,Transform会自动转换为Rect Transform。Rect Transform尽量整合了X-UI常见的anchor(相对父物体的锚点), pivot(中点), stretch(拉伸)等属性。值得一提的是,这里的anchor是Rect而非Vector2,因为它不仅用于偏移,而且用于缩放。点击Rect Transform上的准心图标,还能在弹出的Anchor Presets面板中对其进行快速设置。
这个面板还是不够直观,我们可以把它看成一张表,上面四个图标用于设置列,左边四个图标用于设置行,也可以直接点击里面的16个图标同时设置行和列。强大的地方是,按住shift时能同时设置pivot,这时能发现控件虽然不动但position已经在改变。如果按住alt,则设置anchor的同时设置position。如果shift和alt同时按住,那么你就能同时设置anchor, pivot和position。这个操作方式比起X-UI,真的高明很多,对多分辨率适配很有帮助。
除此之外,Rect Transform还提供了Blueprint和Lock Rect选项,前者用于对旋转过的元素进行定位,后者据说明是能在设置anchor时保持位置不变,暂时没搞明白。
uGUI可以直接在Hierarchy面板中上下拖拽来对渲染进行排序(支持程序控制),越上面的UI会越先被渲染,相比X-UI的global depth排序,这样的拖拽设计很讨好用户。同时在结构上则和ex2D采用的local depth类似,这样GO只和同级其它GO进行排序,开发组件会很方便。需要注意的是,这里排序只是相对UI而言,其它3D物体还是按原先的次序渲染,并且UI总是渲染在3D物体上面。这就导致你不能像用ex2D那样直接将粒子系统插入到两个UI之间。
这种无需填写depth值的排序方式,容易导致没有手工做sprite packing的free版用户遇到draw call增加。因为所有物体的depth都是自动设置的,Unity保证了每个物体的depth都是唯一的。这时假设你有一个格子控件,每个控件用到了两个Sprite,但你并没有把Sprite都拼到同一张贴图上。于是你每复制一个新的格子出来,draw call就会增加2个,因为Unity会以格子为单位依次绘制。pro用户由于有sprite packing机制,不用担心这个问题。(这种情况在ex2D里,是以默认提供"unordered"的渲染方式来解决的,这也是NGUI的默认做法。在这种情况下ex2D会优先以相同depth的相同Sprite为单位绘制,因此不论有多少个格子,draw call都是2个。除非你就是希望以格子为单位进行渲染[注6],那么你可以在ex2D里设置渲染方式为"ordered",或者在NGUI里给每个格子设置不同的depth。
uGUI自带了以上控件,其中Image用于显示Sprite,Raw Image用于显示Texture,Image Mask和Rect Mask用于clipping。所有控件都是MonoBehaviour,可以直接从Inspector里拖到其它GameObject上。
uGUI用Image控件显示图片,图片就是一个Sprite,这意味着Pro用户不用再制作atlas了,相比X-UI是个大进步,Free用户一样可以手动做Packing。Image提供了Simple, Sliced, Tiled, Filled四种效果,和X-UI保持一致。
uGUI里,Button控件由两个GameObject组成,一个包含Image, Button等Component,一个包含Text等Component。这样设计很组件化,唯一的问题是当用户想修改Button时,容易不小心选中Label或其它实体。
Button Component主要执行Transition和事件两个操作。
uGUI控件往往只提供一个自带事件,要响应更多基本事件的话,需要添加Event Trigger组件。Event Trigger包含以下事件:
可以在Event Trigger中Add多个事件,每个事件都可以添加多个命令,用法和控件自带事件一致。
每个Canvas都有一个Graphic Raycaster,用于获取用户选中的uGUI控件。多个Canvas之间通过设置Graphic Raycaster的priority来设置事件响应的先后次序。当Canvas采用World Space或Camera Space时,Graphic Raycaster的Block选项可以用来设置遮挡目标。
创建uGUI控件后,Unity会同时创建一个[注4]叫EventSystem的GameObject,用于控制各类事件。可以看到Unity自带了两个Input Module,一个用于响应标准输入,一个用于响应触摸操作。Input Module封装了对Input模块的调用,根据用户操作触发各Event Trigger。理论上我们可以编写自己的Input Module,用来封装各种外部设备的输入,只要加入Event System所在的GameObject就行。
Event System组件则统一管理多个Input Module和各种Raycaster。它每一帧调用多个Input Module处理用户操作,也负责调用多个Raycaster用于获取用户点击的uGUI控件以及2D和3D物体。
2D渲染分两大类,一类是单纯的Sprite绘制,用于渲染场景、角色、粒子等,另一类是UI绘制。Unity将这两类需求划分成了SpriteRenderer和uGUI两部分,前者由Transform + SpriteRenderer实现,后者由Rect Transform +CanvasRenderer + UI控件 + Canvas[注2]实现,这样的两套相对独立的机制比起X-UI的UI控件继承自SpriteRenderer更为合理。因为在2D游戏里SpriteRenderer只需要关心最基本的面片渲染,注重效率,而UI注重各类变换、对齐、操作、动画,还常常需要Resize VBO。如果SpriteRenderer在设计上需要兼顾UI,就会像X-UI那样设计得太过复杂,在用户体验和性能上都很不好。
这里我们探讨一下uGUI的渲染机制,当我们渲染多个使用相同Sprite的控件时,并没发生dynamic batching,但是drawcall也没有上升。这就说明Unity在内部使用了专门的一套batching机制,把多个控件的VBO事先合并成了一个。也就是说CanvasRenderer不负责实际渲染,而是由Canvas批量渲染多个CanvasRenderer,这和部分X-UI采用的做法一致。这样单独batch的设计有可能使得性能比SpriteRenderer好,也可能导致性能更差。性能会更好的情况在ex2D里已经证实了,主要原因是这样能更好的平衡CPU和GPU负载,并且能做到更优化的batching算法。性能更差的情况,在去年旧版的NGUI测试时也遇到了,根本原因还是优化不到位导致的(不是贬低,不同工具的取舍和面向市场都不同)。而Unity的SpriteRenderer在手机上的渲染跑分是和ex2D持平的,CanvasRenderer又比SpriteRenderer快[注3],因此uGUI的性能不用担心。由于目前没有Mac版本,我会在正式版发布后进行一次手机跑分测试。
uGUI功能完善,操作简洁,很接地气。可以说uGUI是相对X-UI的全面升级,整体架构更为严谨,实现更为清晰。依托4.5的Module Manager,uGUI以Package的形式提供,也能获得快速的升级[注5]。作为ex2D v2.0开发者之一,我很看好它将来的发展,uGUI将在大多数场合取代X-UI。
初步感受:
Jare @ 梦加网络