Unity UI架构设计理念

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) 对显示语言的重构。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,391评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 初白家搬了新家,住到了村北,这里离村子中心还有二里多路,还鲜有人家。今天村里有不少亲戚都要去帮忙,所以大家要先到村...
    老铁的五月麦田阅读 197评论 0 1
  • 为什么喜欢绘画?这么久了,终于,我算是找到了令自己都感到满意的答案了。 鹿儿老师说,虽然水彩的技法上还有不少欠缺,...
    小曲奇儿阅读 347评论 0 1
  • 有这样一种现象,买房子是为了讨媳妇,好像买了房子一定会找到一个自己喜欢的姑娘结婚,读大学,是为了找一份好工作,好像...
    绿芽茁壮阅读 1,218评论 6 3