简易 Unity3D UI 框架

看见一篇介绍 Unity3D UI 框架编写的文章,并且给出了示例代码。然后去了解了一下。讲道理,示例代码蛮乱的,不知道有一些是不是直接从项目代码拷贝然后简单修改,对于一个简单的框架来说,有很多不必要的混乱逻辑。而且我又喜欢粉色,所以就重新编写了,话不多说先上运行图示。具体代码点这里。

运行图示

想了解更多信息具体查看代码。这篇博客主要谈一谈 UI 框架基本的需求。UI 框架有很多实现方式,比如多个界面共用一个摄像机渲染还是单个界面单个摄像机渲染,但是基本的需求是一致的。掌握了基本的需求,才能适应不同项目策划百变的需求。最后需求分析基于手游,界面基于 NGUI

基本情景

简易 Unity3D UI 框架_第1张图片
UIOper

游戏玩法一般分为 UI 玩法和核心玩法。假设核心玩法是战斗。UI 玩法与核心相互交织连接,比如打开装备界面锻炼装备,这样战斗时就更厉害,还比如打开聊天界面与好友聊天,一起组队打 Boss 。上图是常见的操作之一,总结为两句话:界面之间跳转,界面与战斗跳转,这是最基本的需求情景。

了解了基本情景后来探索更多的细节吧。

界面渲染

渲染是最现实的问题,毕竟界面要先能被看见。新创建一个 Camera 独立负责界面渲染,和主场景和战斗场景渲染独立开来。对于这个只渲染 UI 的 Camera 设置 Camera.cullingMask 指定渲染的 Layer,避免冗余渲染,同样界面(GameObject)的 Layer 也要设置为值才能被显示 。这样当调节战斗场景和主场景摄像机渲染时,UI 不会受到影响,相应的修改 UI Camera 的选项时,也不会影响非 UI 的渲染。

界面跳转与界面层级(depth,深度值)

界面跳转与界面层级是相互关联的。比如某个导航按钮打开一个新的界面,但新的界面层级比较低,这样在重叠区域导航按钮就会遮挡新打开的界面,这不符合实际需求,因此需要合理的管理界面层级,防止出现错误的遮挡。界面跳转涉及多个界面,界面层级涉及到渲染次序进而影响显示顺序,NGUI 中可以用 depth 控制渲染次序,UGUI 又是另一种方式控制渲染次序,这里使用 NGUI 深度值进行描述,目的都是一样的,就是控制渲染秩序。

当游戏界面足够的简单,UI 管理就更简单。如下所示。比如仅仅创建两个 UIPanel,ToolPanel 深度值更大,显示优先级更高,PlayPanel 优先级低一些,用来展示玩法,由于游戏界面很简单,此时完全可以把所有界面都创建好,如 UI1 和 UI2 。比如需要显示 UI2 时,设置 UI1 为 Inactive 然后设置 UI2 为 Active 。通过这种方式,你就可以完成一个超简单的 UI 框架,当然它的功能备受限制,但是对于简单的小游戏来说,也足够了。

UIRoot
    PlayPanel
        UI1
        UI2
    ToolPanel
        Tool

然而很多时候游戏会涉及很多界面。这样就得考虑更灵活的实现方式。

  • 缩放选择。NGUI 使用 UIRoot 组件进行缩放。因此完全可以所有界面共用一个 UIRoot 组件。如下格式。Panel1 到 Panel3 是三个不同的界面。
UIRoot
    Panel1
        Panel2
        Panel3

当然若不在乎冗余的 UIRoot 组件,或者想个别界面采用特俗的缩放方式,也可以每个节目单独一个 UIRoot 组件。如下。

UIRoot1
    Panel
UIRoot2
       Panel
UIRoot3
       Panel
  • 摄像机选择。再提一下,摄像机指定 Layer 且 UI 界面 GameObject 的 Layer 设置为值。
  • 每个界面单独一个摄像机。设置界面所在摄像机的 Camera.depth 即可设置界面层级。这种方式需要避免摄像机错乱,例如界面 A 的摄像机又渲染了界面 B 。有一种解决方法是设置 UIRoot 的位置,避免不同界面出现重叠。
  • 所有界面共用单个摄像机。修改界面层级通过设置 UIPanel.depth 。这种方式设置界面层级时需要设置该界面所有 UIPanel.depth 。比如设置 depth 是 1000,则需要排序该界面所有 UIPanel 然后从小到大分别设置为 1000 、1001 、1002 等。
  • 界面类型。游戏中存在不同类型的界面,有些界面就应该具有更高的优先级,遮挡住其他界面,例如导航界面。还有一种常见的就是玩法的界面,不同的玩法界面相对独立。比如当界面 A 与界面 B 交互时,此时采用窗口跳转即可。
  • 界面跳转。打开新界面时,是否禁用其他渲染。关闭新界面时,恢复之前的渲染状态
  • 禁用其他渲染(包括禁用主场景渲染)。打开全屏窗口时可以采用此方式。此时设置新界面层级,可以直接设置深度值。
  • 不禁用其他渲染。打开非全屏窗口时可以采用此方式。此时设置新界面层级,需要动态计算深度值(可以递增深度值)并设置到界面,使新界面可以遮挡其他相应界面显示。
  • 界面深度值管理。NGUI 采用深度值。UGUI 是另外一种方式。总之目的是实现合理的界面遮挡。
  • 界面与战斗。进入战斗后,一般之前的那些玩法界面都需要被隐藏,取而代之的是用于战斗中的界面。退出战斗后关闭战斗中的界面,然后再恢复进入战斗前的玩法界面状态。当然了进入战斗后如果退出了未显示场景,此时可以停止主场景的渲染。
  • 界面与消息屏蔽。比如界面 A 显示在界面 B 的上方,一般情况下肯定都不想在界面 A 上随便点击,却触发了界面 B 上的事件,对吧。因此这一项也是需要考虑的。

这些都是基本的 UI 框架需求。这些需求可以根据项目情况或简单或复杂实现。相信读到这里,你内心也有了一些自己的想法了,是吧。

再进一步,资源,内存,效率

  • 界面资源。实际项目中很多界面,不可能全部载入到内存,因此都是用到时才加载。若所有界面被单一个摄像机渲染,资源文件就不用包含摄像机,只需包含相应的面板即可。
  • 资源可被保存到 .unity 文件。使用时加载 .unity 文件。
  • 资源也可被保存到 prefab 文件中。
  • 界面内存。若界面不多,使用的界面就一直存放在内存中。若需要考虑性能,则要处理资源销毁从而腾出内存空间。
  • 效率问题。若界面很大且包含了更多的内容,打开界面时若全部加载所有内容,会导致界面打开比较慢。此时就可以考虑界面拆分。先载入必须显示的内容,剩下的内容根据需要动态加载。这一步可以单个界面自己负责实现,也可以考虑到整合到框架中,方便使用的开发者。

开发

到这一步一个框架就开发出来使用了。在实际项目需求中可能会为了便利性,进一步增加一些通用功能。下面列举一些项目中会实际处理的事情。

  • 打开界面时,传递参数。
  • 打开和关闭界面时,播放动画。
  • 界面的刷新,包括玩法上的主动刷新(如获取新的道具刷新背包)或者断线重连后的刷新。
  • 界面的重复打开。
  • 界面显示特效。
  • 界面显示模型。

下面是前面提到的我重新实现的小框架的例子。例子很简单,就是显示一个界面。在 Awake 中进行初始化处理即可。之后便可以调用 WndMgr.inst.ShowWindow(WndId.shop); 来显示界面。

using UnityEngine;
using TinyWnd;

public class ShopWnd : Wnd
{
    protected override void OnAwake()
    {
        _wndId = WndId.shop;
        _colliderMode = WndColliderMode.transparent;
        _closeClickBg = true;
        _unifiedClose = true;
    }
}

提一点,组件中 AwakeStart 执行顺序,大家都知道。要注意的地方是 AwakeStart 不一定是在同一帧执行。当调用 AddComponent 添加新的组件时,初始化完毕后 Awake 会被调用,而 Start 未被调用。因此组件的一些初始化处理放在 Awake 函数中会更加合适。这样的话,动态添加这个组件后就可以使用了,而不用担心一些初始化操作未被执行。


我的博客地址 https://my.oschina.net/iirecord/blog

你可能感兴趣的:(简易 Unity3D UI 框架)