天天飞车之使用NGUI制作UI

2017年05月09日 17:59 0 点赞 0 评论 更新于 2025-11-21 21:25

为了加深各位同学对NGUI系统的了解,下面先对其进行简单介绍。NGUI全称为Next-Gen UI,意为下一代UI系统,从名称可以看出开发者对该作品充满自信。实际上,这个系统(也可以称为这套代码或解决方案)确实有值得自信的资本,它至少具备以下几个优势:

  1. 高效的组织架构:能够很好地组织游戏内UI的各个元件和模块,提供了一套完整的解决方案。
  2. 卓越的性能表现:直接使用引擎的绘图接口,具备极佳的性能。
  3. 清晰的设计理念:设计合理,概念清晰,易于理解。

虽然NGUI是基于Unity3D引擎开发的,但它的设计思想具有一定的前瞻性,同样的设计思想也可应用于其他引擎。在Unity3D内使用NGUI制作UI界面时,我们仍然会使用Unity3D引擎本身定义的元素,如GameObject、Mesh、Transform等进行组合。NGUI基于这些元素,按照自身的方式进行组合和运行。因此,如何按照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需要解决多分辨率适配问题。例如,美术同学按照iPhone 4S的基础分辨率(960 640)设定的UI,在iPad(1024 768)上也必须能正确显示。由于这两种分辨率的长宽比不同,若要使某个元件自动靠边,就需要正确设定其锚点。

通常情况下,所需的锚点数量不会很多。按照常见设计(如魔兽世界的UI系统或NGUI 2.2.0的设定),锚点共有9个,分别为上下左右、左上、右上、左下、右下和中间(新版本的NGUI中,锚点机制略有不同,后续再进行讨论)。为保证显示正确,理论上所有元件都应挂载在某个锚点之下,若不在任何锚点之下,则默认显示在屏幕中间。

因此,完整的组织形式为:UIRoot -> UICamera -> UIPanel -> UIAnchor -> UIWidget。不过,需要注意的是,锚点和UIPanel的位置可根据实际业务需求灵活调整。例如,以下形式也是可行的:UIRoot -> UICamera -> UIAnchor -> 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版本,因此需要单独重点说明)。

NGUI为了保证渲染效率,会将同一个Panel下的所有子物体合并成一个Mesh进行渲染。因此,当同一个Panel下使用两个不同的Atlas图集时,渲染次序就不再仅仅由组织的树形结构决定,Z轴负方向上值更大的元素会显示在前面。

作者信息

孟子菇凉

孟子菇凉

共发布了 3994 篇文章