为了加深各位同学对这套系统的了解,先简单介绍一下 NGUI , NGUI 全称是( Next-Gen UI )意为下一代 UI 系统,可见该作者对他这个作品多么自信。
不过这个系统(或这套代码,或这套解决方案)确实有自信的资本,它至少有如下几个优势:
1. 它很好的组织了游戏内 UI 的各个元件、模块,提供了完整的解决方案
2. 它直接使用引擎的绘图接口,有极佳的性能
3. 它设计合理,概念清晰,易于理解
NGUI 虽然是基于 unity3d 引擎来开发的,但它的设计思想有一定的前瞻性,同样的设计思想,也可以应用到其他引擎。就 Unity3d 内使用 NGUI 制作 UI 界面这一套流程来说,我们仍然是使用Unity3d 引擎本身定义的元素(比如 GameObject 、 Mesh 、 Transform 等)来进行组合(拼装), NGUI 只是基于 Unity3d 引擎的这些元素,按照自己的方式组合和运行,因此,如何组合这些元素(按 NGUI 的规范要求),是我们主要考虑的问题和需要学习的点(当然,了解 Unity3d 也是必须的)。
按照 NGUI 的设计和游戏制作的要求,我们在拼装界面时,至少需要考虑以下两个点:
1. 性能问题(影响游戏运行的效率,一定是排在首位)
2. 组织形式问题(根据游戏内使用的要求(程序、动画),合理的组织 UI 元素)
当然,对于美术同学还有效果问题,但是效果始终和性能对立,在我们游戏中,为了缩小包量,节省内存,不仅贴图大量的使用九宫格,贴图的品质也进行了压缩,因此,最终效果与设计图产生了一定差距。
不过此问题并非不能解决,如果从设计之初,就开始考虑九宫格复用的问题,贴图共用的问题,减少相同或相似贴图素材的重复设计,避免重复劳动,更有效的组织资源,强化贴图资源管理,多在设计上做优化,避免使用大块、大面积的贴图、避免图案四周大面积留白,尽量用小而精的元素来组合,这样就可以在保证设计效果的同时,又缩减贴图总量,更有效的利用贴图资源。
NGUI 界面在引擎中是按照树形结构来组织,如图:
图中所有的节点都是一个 GameObject ,只是这些节点有不同的类型(上面挂载了不同的Component ),比如,根节点一定是 UIRoot ( NGUI 的根节点,当点选到该节点上,就可以看到检视视图中显示出了这个 Component )。如图:
根节点之下一般会挂一个相机,用来渲染 UI (有时候会挂两个,用来渲染不同的层,如上图所示),然后至少需要一个 UIPanel ,用来实际绘制这个 UIPanel 之下的各个元件
如图所示 MenuPopUp 就是一个 UIPanel ,在这个节点下的所有子节点,如果是 UIWidget 的派生类型(比如 UILabel 、 UISprite )都会被这个 Panel 实际绘制出来,图中该 Panel 下挂载了 9 个Widgets (元件),将使用 2 个 Draw Calls 绘制, Draw Calls 可看作性能指标,越少越好。
因此, NGUI 的一般组织形式就是:
UIRoot ->UICamera ->UIPanel ->UIWidget
同时有这四个基本元素,就可以在编辑器内看到你需要绘制的 UI 了。以上图所示的结构为例,看到的图案如下:
在这四个基本元素之外,还有一个重要的元素需要单独来说,就是 UIAnchor (锚点),锚点负责 UI的对齐功能,比如某一个小元件需要对齐右上角,另外一个需要对齐右下角,则这两个元件,分别隶属于两个锚点。那么为什么需要锚点呢?因为,游戏内 UI 都需要解决多分辨率适配的问题。比如,美术同学按照 iphone4s 的基础分辨率设定的 UI ( 960 640 )在 iPad 上必须能正确显示( 1024 * 768 ),这两种分辨率的长宽比不同,如果需要某个元件能自动靠边,则需要正确的设定它的锚点。
因此,所需的锚点不会很多,按照通常的设计(比如魔兽世界的 UI 系统或 NGUI 2.2.0 的设定)锚点只有 9 个,上下左右,左上、右上、左下、右下,中间(新版本的 NGUI 中,锚点的机制略有不同,我们之后讨论),要保证显示正确,理论上所有的元件,都需要挂在某个锚点之下,如果不在任何锚点之下,则默认显示在屏幕中间。
因此,完整的组织形式是:
UIRoot ->UICamera ->UIPanel ->UIAchor ->UIWidget
这个形式是完整的,没有其他内容了。但值得注意的是,锚点和 UIPanel 的位置可以根据实际业务需求灵活变化。比如如下形式也是可以的:
UIRoot ->UICamera ->UIAchor ->UIPanel ->UIWidget
这两种形式的区别是,第一个形式所有的元件都在同一个 Panel 中绘制,这样有最佳的渲染效率,以我们项目为例,游戏内 UI 就用这种形式。第二个形式有最好的灵活性,我们的车库界面由于有众多的Menu ,每个 Menu 都是一个单独的功能块,一个 Menu 中可能存在多个部件需要对齐不同的点的情况,所以用第二种形式组织,这个原则在新老版本的 NGUI 中都是如此。
由于游戏内 UI 改动较小,不大会大规模制作 UI 功能,所以美术同学可能会重点关注第二种组织形式下如何正确的组织和建立 UI 。
基本的工作流程是,将整理好的切图放到项目某一个目录中,然后用 NGUI 的 Atlas Maker 来建立(或更新)图集( Atlas ),然后用 NGUI 的 Create a widget 来建立各种元件(组合成树),在编辑器中调整到最佳的效果。 Atlas 如下图所示:
Atlas Maker 会自动将美术同学切好的切图合并为一整张贴图,最重要的是,他会自动使用最佳的拼合方式来最大限度的利用这张贴图的空间,因此,小而精,并且四周不要留白,是节省贴图大小的关键。在我们的项目中,切图资源放在 Pic 目录中,这部分资源不会被打包到最终的游戏包中, Atlas Maker 但生成的 CarIcon.png 这张大图会被打包到最终的游戏包中,引擎打包时会做依赖关系检测。
以做一个按钮为例,可以点击编辑器菜单栏中 NGUI ,选择 Create a widget ,在弹出面板中指定图集( Atlas ),指定字体,选择模版为 Button ,然后点击 Add To ,这样就在你当前选择的GameObject 下建立了一个 Button 元件。
然后需要调整这个 Button 到最终效果, Button 的第一个子节点是 Background 是背景图片,一般是 UISlicedSprite 类型,这个类型的 Sprite 是九宫格伸缩型,可以自由拖动大小,第二个子节点是Label 用来显示按钮上的文字。
其他类型的元件,依此类推,就不一一赘述了。
重点说一下九宫格,按上图所示, Background 是按钮 Button 的背景图片,这张图片的大小是25*80 像素,但显示出来的效果如下:
这个 UISlicedSprite 会自动按照设定的九宫格拉伸中间的区域,这样就可以用一张很小的贴图来做一个很大的元素,以上图的对话框为例,总共使用了 3 张切图,都是九宫格方式制作,总贴图大小不超过 100*100 像素。在我们项目中,可共用的九宫格切图,单独放在一张 Common Atlas 贴图中,这样供所有的 Menu 来使用,不用重复占用内存。如下图所示:
在 UI 制作的过程中经常会遇到一个棘手的问题“显示层级错误”,应该在前面的元件,却被后面的元件挡住了,这个问题是由于元件和 Panel 的组织结构错误引起的(需要说明的是 NGUI 的最新版本中已经从 Panel 这一层解决了此问题,可以保证渲染层次正确,但由于我们项目使用的 Unity3d 3.5.7版本,因此不可能升级到最新的 NGUI 版本,所以此问题需要单独重点说明)