Unity UI架构设计理念
1.以ARPG为例,多个场景会反复出现相同的“UI窗体”,造成多个场景中反复加载相同的UI窗体。
解决方案:
“UI框架” 需要缓存项目(例游戏项目)中常用的“UI窗体"。
2.开发商业复杂项目时,各个UI(UI脚本)之间传值,容易出现“紧耦合”(相互交错,你中有我,我中有你)的情况。
解决方案:
各个UI的生成、销毁、切换,都是通过框架(Manger)实现,各个UI之间不直接联系(传值)。
3.卡牌、RPG等游戏类型项目,很多情况下会出现“UI窗体” 叠加现象。开发人员需要“手工”维护窗体中间的层级关系。
解决方案:
设计UI框架系统,使用“栈”的数据结构,保存与控制当前所有需要显示的“UI窗体”的层级关系。
4.商业开发项目中的多个“UI窗体” 之间叠加出现时,必须保持“模态窗口”类型,否则容易出现误操作。
解决方案
设计的框架本身,需要对当前显示的窗体做“遮挡处理”,即:不允许用户绕过当前
“UI窗体”直接操作底层窗体,或者误操作点击项目中的3D游戏对象等
5.从以上问题还可以推导出如下“UI框架”需要注意的设计问题:
UI框架,需要管理加载“窗体预设”,进行自动加载的管理
UI框架,需要支持不同的语言环境,即语言的国际化。
最后设计UI框架一个总的核心原则是:
尽量让框架本身完成与具体业务无关的事务性工作,让开发人员只需要专注游戏业务逻辑的开发即可。(这个原则同样适用于其他框架的设计中)
首先开发最简版本功能设计:
1:窗体自动加载管理。
2:缓存UI窗体。
3:窗体生命周期管理。
UI框架的核心类设计
1: BaseUIForms 基础UI窗体(父类)
2: UIManger.cs UI窗体管理器
3: UIType 窗体类型
4: SysDefine 系统定义类
在Unity5.5安装目录下,建立脚本模版。
建立必要的目录结构与核心类,导入素材。
BaseUIForms.cs
UIManager.cs
UIType.cs
SysDefine.cs [Config目录下]
导入UI贴图素材。
导入一些简单的UI素材即可。
建立框架中的三个重要枚举类型,定义 UIType 类。
UIFormsType UI窗体(位置)类型
UIFormsShowMode UI窗体显示类型
UIFormsLucencyType 窗体透明度类型
[提示: SysDefine 中定义]
定义“基础UI窗体” BaseUIForms
定义 “UI管理器” UIManager
1: 定义“窗体路径”与“窗体预设”的集合字段。
2:定义“窗体预设”与管理脚本加载用的节点对象。
普通节点、固定节点、弹出节点、管理脚本节点。
3:Unity编辑器中,定义“Canvas 根窗体”预设。
1>在测试场景中建立UI Panel 。
2>共建立3个Panel,与一个挂载脚本的空对象。
3>建立UI摄像机,设置参数。
4> Game视图定义800*600 分辨率。
5>针对Canvas UI对象,设置合理参数
定义 “UI管理器” UIManager (续)
4:定义“登陆窗体”、“选择角色窗体”、“主窗体”等。
1>建立各个窗体的Panel (注意:必须在Canvas 内部建立)
2>给各个窗体添加背景贴图与必要按钮等。
3>定义的窗体都作为“预设”
定义 “UI管理器” UIManager (续)
5:继续开发UIManger 脚本,实现窗体的加载功能,且测试。
1>主场景中,确保拖拽到层级视图中的Canvas预设正确显示。
2>编写Awake 事件函数,对于常量都统一定义在SysDefine中。
3>编写“显示UI窗体”公共方法。
4>框架外建立测试脚本,测试UI窗体的基本加载功能。
1]建立框架外启动加载脚本。
2]针对每个“窗体预设”都需要建立对应的窗体脚本(继承BaseUIForms)
[备注:如果出现“baseUIForms==null, 请先确认克隆对象上是否加载了BaseUIForms的子类”,说明“窗体预设”上必须添加BaseUIForms 的子类]
}定义 “UI管理器” UIManager (续)
5:继续开发UIManger 脚本,实现窗体的加载功能,且测试。(续)
5> 测试以上所有步骤,成功如下图所属。
窗体层级管理
什么是“栈”数据结构?
是一种“先进后出”的数据结构,是一种常用算法。
生活中的“汉诺塔”游戏、“摞烧饼”、“盘子堆”都是一种典型的“栈”结构。
C#语言中提供 Stack泛型集合,来直接实现这种结构。
常用属性与方法:
Count 属性 查询栈内元素数量
Push() 压栈
Pop() 出栈
Peek() 查询栈顶元素
nGetEnumerator() 遍历栈中所有元素
演示:典型Demo示例。
开发“UI管理器”的“栈”数据结构,维护窗体的层级结构。
定义Stack 类型字段。
显示UI窗体 ShowUIForms() 方法中
1>“反向切换”属性的窗体,定义“压栈”方法
关闭(或返回上一个UI)窗体方法中
1>“普通”显示属性的窗体,定义关闭方法。
2>对于“反向切换”属性的窗体,定义返回上一个窗体的方法(即:关闭)。
显示UI窗体 ShowUIForms() 方法中
1>“反向切换”属性窗体,定义“压栈”方法
2> “隐藏其他”属性窗体,定义显示业务逻辑方法
关闭(或返回上一个UI)窗体方法中
1>“普通”显示属性的窗体,定义关闭方法。
2>对于“反向切换”属性的窗体,定义返回上一个窗体的方法。 (即:关闭)。
3>“隐藏其他”属性窗体,定义关闭逻辑方法
在多个UI业务窗体中,有时候需要客户端程序主动清空“栈集合”中的当前数据,防止业务逻辑混乱。
例如: RPG中的“商场系统”、“背包系统”、“任务系统”等。
具体代码实现:
1:在UIType 类中,定义是否需要“清空反向切换”的字段(或者属性)。
2: 在UI管理器脚本中,关于显示UI窗体的方法中,加入判断清空栈中数据的业务逻辑即可。
定义如下窗体编写代码测试UI框架功能:
Ø登陆窗体:
注意事项: 所有窗体脚本都要继承BaseUIForms
定义本窗体的类型(位置、显示、透明度三大属性),不写则采用默认数值。
` 注册窗体按钮事件。
Ø选择英雄窗体:
Ø主城窗体:
Ø商城窗体:
Ø商品信息窗体:
Ø程序重构发现,在UI框架内部与客户调用程序中都存在一些反复被使用的技术。
对于层级视图的节点查找。
(扩展方法)
获取子节点(物体)的脚本
给子节点(物体)添加脚本
给子节点(物体)添加父对象
Ø程序重构发现,在客户程序(UI框架)调用中,会反复出现一些常用的定义方式,例如:
按钮的事件监听与注册方法
打开指定窗体
关闭指定窗体
发送消息
显示语言信息
模态窗体管理
UI窗体中,很多弹出窗体要求玩家不能点击“父窗体”,这就是“模态窗体”。
这里我们设计了四种模式类型:
完全透明、半透明、低透明度、透明且可以穿透。
在Canvas根窗体预设中(PopUp节点下)定义“UI遮挡面板”(_UIMaskPanel)窗体。
“UI遮挡面板” 就是一个普通的Panel。
平时这个面板是“不可见”状态。
当需要进行“模态”显示的时候,则定义脚本,控制其在PopUp节点下倒数第二的位置,起到遮挡作用。
定义一个专门的控制脚本:“UIMaskMgr.cs”,以及在“窗体基类”(BaseUIForms.cs) 中控制“遮挡面板”的显示与隐藏。
框架配置管理
所谓“配置管理”是指一个游戏项目(软件项目),很多需要经常变化的需求或者数据,最好以配置文件的形式存在,从而代替“硬编码”方式。
例如: 游戏项目语言的国际化、
日志文件的保存路径等。
目前(2017)国际国内普遍采用的配置管理方式主要有两种: XML与Json 方式。
两者各有优缺点:
XML:对于数据的精确表示、易读性很高。
微软很多的项目都内置对XML作为配置文件的支持。
(例如:网站项目:ASP.Net、 WinForm 等)
缺点是读写速度慢,这个问题在移动端尤其突出。
Json: 读写速度快,但是易读性没有XML好,但是可以接受。
所以本框架项目都采用Json作为配置文件。
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C、C++、C#、Java、JavaScript、Perl、Python等)。这些特性使JSON成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成(一般用于提升网络传输速率)。
JSON 语法 (JSON 语法是 JavaScript 对象表示语法的子集)
特点:
数据在键值对中,数据由逗号分隔。
花括号保存对象,方括号保存数组。
JSON 数据的书写格式是:名称/值对。 "firstName":"John"
开发Json配置管理器
定义通用配置管理器接口
开发实现IConfigManager 接口的通用配置管理器
UI管理器中关于“UI窗体预设路径”集合中,把前面“硬编码”改为应用Json 配置管理的方式。
第1步在Resources 目录下建立关于“UIFormsConfigInfo”的Json 文件。
第2步对于UIManager.cs 中的“UI窗体预设路径”集合做配置管理。
日志调试
日志调试在游戏的开发全过程中占有非常重要的作用。
自定义“日志调试”脚本插件的开发思路与具体实现。
现在为了更好的适用于PC与移动端调试的目的,进行再次重构。
第1:对于读写文件内部方法做重构完善,使得日志文件实现自动侦测与创建写入操作等。
第2: 改以前的针对XML的配置文件的读取方式为Json 文件的读取。
目的一:提高读取速度。
二: 更好的应用在移动端的部署
消息传递中心
基于Unity技术的游戏与项目研发,目前提供的消息传递方式种类少,且耦合性很高。
1:脚本组件公共方法、字段的相互调用。
例如: GetComponnet().TestMethod();
2:SendMesage 技术。
3:单例模式数据传递。
开发一种低耦合,无需考虑被传递对象(脚本名称、组件名称)的技术非常有价值。
“消息传递中心”:
基于观察者模式,利用委托与事件的基本机制原理,进一步封装重构的技术实现。
定义MessageCenter
基本原理:
窗体基类(BaseUIForms) 中对于“消息传递中心”类常用方法的封装。
发送消息 SendMessage()
接收消息 ReceiveMessage()
客户程序消息传递多组数据的演示。
客户程序建立“系统常量”类,方便程序复用与集中化管理
资源国际化
“资源国际化”对于游戏项目开发是指:语言、语音、贴图、模型等国际化问题。
对于游戏项目,最常见的是针对不同国家的多语言版本的开发,也就是“语言的国际化”。
多语言版本的实现,最基本的原理就是根据ID去读取语言配置表,不同的语言新建一个语言配置表。
定义“语言管理器”(LanguageMgr)
基本原理:
1: 使用配置管理器脚本(继承 IConfigManager接口),读取不同语言的Json配置文件。
2: 使用 Dictionary 集合缓存“语言键值对”。
3:定义显示方法,根据ID查询出对应的语言信息
UI窗体基类(BaseUIForms) 对显示语言的重构。