游戏UI框架设计(二) : 最简版本设计
游戏UI框架设计(二)
--最简版本设计
为降低难度决定先讲解一个最简版本,阐述UI框架的核心设计理念。这里先定义三个核心功能:
1:UI窗体的自动加载功能。
2:缓存UI窗体。
3:窗体生命周期(状态)管理。
UI框架设计主要目的,就是尽可能的完成一些与具体游戏功能逻辑无关的一些底层事务性的功能实现。这些功能最好是自动或者是半自动的实现,无须客户程序(调用框架的程序)再去过多处理与关心。
对于以上功能,笔者定义了UI框架的相关四个核心类:
- BaseUIForms 基础UI窗体脚本(父类,其他窗体都继承此脚本)
 - UIManger.cs UI窗体管理器脚本(框架核心脚本)
 - UIType 窗体类型 (引用窗体的重要属性[枚举类型])
 - SysDefine 系统定义类(包含框架中使用到的枚举类型、委托事件、系统常量、接口等)
 
在SysDefine 文件中,定义本框架三个核心枚举类型
//UI窗体(位置)类型
public enum UIFormType
{
//普通窗体
Normal,
//固定窗体
Fixed,
//弹出窗体
PopUp
} //UI窗体的显示类型
public enum UIFormShowMode
{
//普通
Normal,
//反向切换
ReverseChange,
//隐藏其他
HideOther
} //UI窗体透明度类型
public enum UIFormLucenyType
{
//完全透明,不能穿透
Lucency,
//半透明,不能穿透
Translucence,
//低透明度,不能穿透
ImPenetrable,
//可以穿透
Pentrate
}
上述三个核心枚举类型,解释如下:
- UIFormType 枚举类型,表示Unity层级视图中挂载不同类型窗体的空节点。这里Fixed 表示固定窗体,表示可以挂载"非全屏非弹出窗体",例如RPG游戏项目中的“英雄信息”窗体等。
 - UIFormShowMode 枚举,表示窗体不同的显示方式。Normal 类型表示窗体与其他窗体可以并列显示; HideOther类型表示窗体显示的时候,需要隐藏所有其他窗体; ReverseChange 窗体主要应用与"弹出窗体",维护多个弹出窗体的层级关系。
 - UIFormLucenyType 枚举,是定义弹出“模态窗体”不同透明度的类型。
 

上图是我们定义的UGUI 中的“根窗体”预设 "Canvas",在Untiy的层级视图中,可以看到我们定义了若干空节点,用于不同类型的UI窗体加载到不同的“根窗体”预设中,实现不同显示效果。
定义 UIType 类,主要是引用定义的三个核心枚举,方便使用 。代码如下:
/// <summary>
/// UI(窗体)类型
/// </summary>
internal class UIType
{
//是否需要清空“反向切换”
public bool IsClearReverseChange = false;
//UI窗体类型
public UIFormsType UIForms_Type = UIFormsType.Normal;
//UI窗体显示类型
public UIFormsShowMode UIForms_ShowMode = UIFormsShowMode.Normal;
//UI窗体透明度类型
public UIFormsLucencyType UIForms_LucencyType = UIFormsLucencyType.Lucency;
}
定义基础UI窗体 BaseUIForms 脚本,代码如下:
public class BaseUIForms : MonoBehaviour
{
/* 字段 */
//当前(基类)窗口的类型
private UIType _CurrentUIType=new UIType(); /* 属性 */
/// <summary>
/// 属性_当前UI窗体类型
/// </summary>
internal UIType CurrentUIType
{
set
{
_CurrentUIType = value;
} get
{
return _CurrentUIType;
}
} //页面显示
public virtual void Display()
{
this.gameObject.SetActive(true);
} //页面隐藏(不在“栈”集合中)
public virtual void Hiding()
{
this.gameObject.SetActive(false);
}
//页面重新显示
public virtual void Redisplay()
{
this.gameObject.SetActive(true);
}
//页面冻结(还在“栈”集合中)
public virtual void Freeze()
{
this.gameObject.SetActive(true);
} }//Class_end
上述代码中,主要定义了UI窗体基类的四个重要虚方法,分别对应窗体的打开显示、隐藏、重新显示、窗体冻结(即:窗体显示在其他窗体下面)。方便窗体在不同状态下,针对不同的行为进一步做处理操作。例如,当窗体为“隐藏”与“冻结”状态时,如果此窗体有针对远程服务的网络连接(Socket套接字)时,则需要关闭网络连接,以节省网络资源。
定义“UI管理器”(UIManager.cs) 脚本,这是UI框架中的核心脚本,主要负责UI窗体的加载、缓存、以及对于“UI窗体基类”的各种生命周期的操作(显示、隐藏、重新显示、冻结)。
 public class UIManager : MonoBehaviour {
         /* 字段 */
         private static UIManager _Instance = null;
         //UI窗体预设路径(参数1:窗体预设名称,2:表示窗体预设路径)
         private Dictionary<string, string> _DicFormsPaths;
         //缓存所有UI窗体
         private Dictionary<string, BaseUIForm> _DicALLUIForms;
         //当前显示的UI窗体
         private Dictionary<string, BaseUIForm> _DicCurrentShowUIForms;
         //UI根节点
         private Transform _TraCanvasTransfrom = null;
         //全屏幕显示的节点
         private Transform _TraNormal = null;
         //固定显示的节点
         private Transform _TraFixed = null;
         //弹出节点
         private Transform _TraPopUp = null;
         //UI管理脚本的节点
         private Transform _TraUIScripts = null;
         /// <summary>
         /// 得到实例
         /// </summary>
         /// <returns></returns>
         public static UIManager GetInstance()
         {
             if (_Instance==null)
             {
                 _Instance = new GameObject("_UIManager").AddComponent<UIManager>();
             }
             return _Instance;
         }
         //初始化核心数据,加载“UI窗体路径”到集合中。
         public void Awake()
         {
             //字段初始化
             _DicALLUIForms=new Dictionary<string, BaseUIForm>();
             _DicCurrentShowUIForms=new Dictionary<string, BaseUIForm>();
             _DicFormsPaths=new Dictionary<string, string>();
             //初始化加载(根UI窗体)Canvas预设
             InitRootCanvasLoading();
             //得到UI根节点、全屏节点、固定节点、弹出节点
             _TraCanvasTransfrom = GameObject.FindGameObjectWithTag(SysDefine.SYS_TAG_CANVAS).transform;
             _TraNormal = _TraCanvasTransfrom.Find("Normal");
             _TraFixed = _TraCanvasTransfrom.Find("Fixed");
             _TraPopUp = _TraCanvasTransfrom.Find("PopUp");
             _TraUIScripts = _TraCanvasTransfrom.Find("_ScriptMgr");
             //把本脚本作为“根UI窗体”的子节点。
             this.gameObject.transform.SetParent(_TraUIScripts, false);
             //"根UI窗体"在场景转换的时候,不允许销毁
             DontDestroyOnLoad(_TraCanvasTransfrom);
             //初始化“UI窗体预设”路径数据
             //先写简单的,后面我们使用Json做配置文件,来完善。
             if (_DicFormsPaths!=null)
             {
                 _DicFormsPaths.Add("LogonUIForm", @"UIPrefabs\LogonUIForm");
             }
         }
         /// <summary>
         /// 显示(打开)UI窗体
         /// 功能:
         /// 1: 根据UI窗体的名称,加载到“所有UI窗体”缓存集合中
         /// 2: 根据不同的UI窗体的“显示模式”,分别作不同的加载处理
         /// </summary>
         /// <param name="uiFormName">UI窗体预设的名称</param>
         public void ShowUIForms(string uiFormName)
         {
             BaseUIForm baseUIForms=null;                    //UI窗体基类
             //参数的检查
             if (string.IsNullOrEmpty(uiFormName)) return;
             //根据UI窗体的名称,加载到“所有UI窗体”缓存集合中
             baseUIForms = LoadFormsToAllUIFormsCatch(uiFormName);
             if (baseUIForms == null) return;
             //根据不同的UI窗体的显示模式,分别作不同的加载处理
             switch (baseUIForms.CurrentUIType.UIForms_ShowMode)
             {
                 case UIFormShowMode.Normal:                 //“普通显示”窗口模式
                     //把当前窗体加载到“当前窗体”集合中。
                     LoadUIToCurrentCache(uiFormName);
                     break;
                 case UIFormShowMode.ReverseChange:          //需要“反向切换”窗口模式
                     //更靠后课程进行讲解。
                     break;
                 case UIFormShowMode.HideOther:              //“隐藏其他”窗口模式
                     //更靠后课程进行讲解。
                     break;
                 default:
                     break;
             }
         }
         #region 私有方法
         //初始化加载(根UI窗体)Canvas预设
         private void InitRootCanvasLoading()
         {
             ResourcesMgr.GetInstance().LoadAsset(SysDefine.SYS_PATH_CANVAS, false);
         }
         /// <summary>
         /// 根据UI窗体的名称,加载到“所有UI窗体”缓存集合中
         /// 功能: 检查“所有UI窗体”集合中,是否已经加载过,否则才加载。
         /// </summary>
         /// <param name="uiFormsName">UI窗体(预设)的名称</param>
         /// <returns></returns>
         private BaseUIForm LoadFormsToAllUIFormsCatch(string uiFormsName)
         {
             BaseUIForm baseUIResult = null;                 //加载的返回UI窗体基类
             _DicALLUIForms.TryGetValue(uiFormsName, out baseUIResult);
             if (baseUIResult==null)
             {
                 //加载指定名称的“UI窗体”
                 baseUIResult = LoadUIForm(uiFormsName);
             }
             return baseUIResult;
         }
         /// <summary>
         /// 加载指定名称的“UI窗体”
         /// 功能:
         ///    1:根据“UI窗体名称”,加载预设克隆体。
         ///    2:根据不同预设克隆体中带的脚本中不同的“位置信息”,加载到“根窗体”下不同的节点。
         ///    3:隐藏刚创建的UI克隆体。
         ///    4:把克隆体,加入到“所有UI窗体”(缓存)集合中。
         ///
         /// </summary>
         /// <param name="uiFormName">UI窗体名称</param>
         private BaseUIForm LoadUIForm(string uiFormName)
         {
             string strUIFormPaths = null;                   //UI窗体路径
             GameObject goCloneUIPrefabs = null;             //创建的UI克隆体预设
             BaseUIForm baseUiForm=null;                     //窗体基类
             //根据UI窗体名称,得到对应的加载路径
             _DicFormsPaths.TryGetValue(uiFormName, out strUIFormPaths);
             //根据“UI窗体名称”,加载“预设克隆体”
             if (!string.IsNullOrEmpty(strUIFormPaths))
             {
                 goCloneUIPrefabs = ResourcesMgr.GetInstance().LoadAsset(strUIFormPaths, false);
             }
             //设置“UI克隆体”的父节点(根据克隆体中带的脚本中不同的“位置信息”)
             if (_TraCanvasTransfrom != null && goCloneUIPrefabs != null)
             {
                 baseUiForm = goCloneUIPrefabs.GetComponent<BaseUIForm>();
                 if (baseUiForm == null)
                 {
                     Debug.Log("baseUiForm==null! ,请先确认窗体预设对象上是否加载了baseUIForm的子类脚本! 参数 uiFormName=" + uiFormName);
                     return null;
                 }
                 switch (baseUiForm.CurrentUIType.UIForms_Type)
                 {
                     case UIFormType.Normal: //普通窗体节点
                         goCloneUIPrefabs.transform.SetParent(_TraNormal, false);
                         break;
                     case UIFormType.Fixed: //固定窗体节点
                         goCloneUIPrefabs.transform.SetParent(_TraFixed, false);
                         break;
                     case UIFormType.PopUp: //弹出窗体节点
                         goCloneUIPrefabs.transform.SetParent(_TraPopUp, false);
                         break;
                     default:
                         break;
                 }
                 //设置隐藏
                 goCloneUIPrefabs.SetActive(false);
                 //把克隆体,加入到“所有UI窗体”(缓存)集合中。
                 _DicALLUIForms.Add(uiFormName, baseUiForm);
                 return baseUiForm;
             }
             else
             {
                 Debug.Log("_TraCanvasTransfrom==null Or goCloneUIPrefabs==null!! ,Plese Check!, 参数uiFormName="+uiFormName);
             }
             Debug.Log("出现不可以预估的错误,请检查,参数 uiFormName="+uiFormName);
             return null;
         }//Mehtod_end
         /// <summary>
         /// 把当前窗体加载到“当前窗体”集合中
         /// </summary>
         /// <param name="uiFormName">窗体预设的名称</param>
         private void LoadUIToCurrentCache(string uiFormName)
         {
             BaseUIForm baseUiForm;                          //UI窗体基类
             BaseUIForm baseUIFormFromAllCache;              //从“所有窗体集合”中得到的窗体
             //如果“正在显示”的集合中,存在整个UI窗体,则直接返回
             _DicCurrentShowUIForms.TryGetValue(uiFormName, out baseUiForm);
             if (baseUiForm != null) return;
             //把当前窗体,加载到“正在显示”集合中
             _DicALLUIForms.TryGetValue(uiFormName, out baseUIFormFromAllCache);
             if (baseUIFormFromAllCache!=null)
             {
                 _DicCurrentShowUIForms.Add(uiFormName, baseUIFormFromAllCache);
                 baseUIFormFromAllCache.Display();           //显示当前窗体
             }
         }
         #endregion
     }//class_end
UI管理器脚本解释如下:
一:上述代码中重要字段的解释如下:
    1:  “_DicFormsPaths” 表示“UI窗体预设路径”集合,负责缓存所有UI窗体预设的名称与对应资源路径的关系。
  2: “ _DicALLUIForms” 表示“所有UI窗体”集合,负责缓存已经加载过的所有UI窗体名称以及与之对应的UI窗体。
  3: “_DicCurrentShowUIForms”表示“当前正在显示”集合,负责控制正在显示UI窗体的内部逻辑。
  4: UI管理器脚本中的“_TraCanvasTransfrom”、“_TraNormal”、“_TraFixed”、“_TraPopUp”、“_TraUIScripts”,分别表示Unity层级视图中的根结点、普通节点、固定节点、弹出节点、管理脚本节点,这些节点是加载UI窗体的不同类型的父节点,用于各种UI窗体的管理工作。
二:上述代码中重要方法的解释如下:
  1: ShowUIForms()  是外部程序调用本框架的对外公共方法,负责加载、缓存、打开与显示制定窗体名称的UI窗体预设。
  2: LoadFormsToAllUIFormsCatch() 是根据UI窗体的名称,加载到“所有UI窗体”缓存集合中。
  3: LoadUIToCurrentCache() 是把当前窗体加载到“当前窗体”集合中。
上述(UI框架)脚本编写完毕,测试成功后效果如下图:

为广大读者进一步了解与熟悉本框架,特提供下载链接:https://pan.baidu.com/s/1eTA8rHS 密码:4x6e
本篇就先写到这,下篇 "游戏UI框架设计(3)_窗体的层级管理" 继续。
游戏UI框架设计(二) : 最简版本设计的更多相关文章
- 游戏UI框架设计(一) : 架构设计理论篇
		
游戏UI框架设计(一) ---架构设计理论篇 前几天(2017年2月)看到一篇文章,国内王健林.马云等大咖们看好的未来十大最有"钱途"产业中,排名第一的就是"泛娱乐&qu ...
 - 游戏UI框架设计(五): 配置管理与应用
		
游戏UI框架设计(五) --配置管理与应用 在开发企业级游戏/VR/AR产品时候,我们总是希望可以总结出一些通用的技术体系,框架结构等,为简化我们的开发起到"四两拨千金"的作用.所 ...
 - 游戏UI框架设计(四) : 模态窗体管理
		
游戏UI框架设计(四) --模态窗体管理 我们在开发UI窗体时,对于"弹出窗体"往往因为需要玩家优先处理弹出小窗体,则要求玩家不能(无法)点击"父窗体",这种窗 ...
 - 游戏UI框架设计(7):  资源国际化技术
		
游戏UI框架设计(7) --资源国际化技术 说起"资源国际化"技术,个人认为可以追述到微软Window2000 PC操作系统的发布,在这之前windows98操作系统的开发都是先由 ...
 - 游戏UI框架设计(6): 消息传递中心
		
游戏UI框架设计(6) --消息传递中心 最近一直忙于一个益智类游戏的研发工作,所以博客有段时间没有更新了.经过朋友的督促,决定这两天立刻完成最后的两篇博客讲解(UI框架).说起“消息传递中心”,或者 ...
 - 游戏UI框架设计(三) : 窗体的层级管理
		
游戏UI框架设计(三) ---窗体的层级管理 UI框架中UI窗体的"层级管理",最核心的问题是如何进行窗体的显示管理.窗体(预设)的显示我们前面定义了三种类型: 普通.隐藏其他.反 ...
 - H+后台主题UI框架---整理(二)
		
本篇文章是针对H+后台主题UI框架的整理的第二部分.主要只有一个point.如下: 其代码如下: <div class="ibox float-e-margins"> ...
 - 手写SpringMVC框架(二)-------结构开发设计
		
续接前文, 手写SpringMVC框架(一)项目搭建 本节我们来开始手写SpringMVC框架的第二阶段:结构开发设计. 新建一个空的springmvc.properties, 里面写我们要扫描的包名 ...
 - 《开源框架那些事儿22》:UI框架设计实战
		
UI是User Interface的缩写.通常被觉得是MVC中View的部分,作用是提供跟人机交互的可视化操作界面. MVC中Model提供内容给UI进行渲染,用户通过UI框架产生响应,一般而言会由控 ...
 
随机推荐
- UISwitch——开关控件
			
开关的可配置选项很少,一般用于处理布尔值. 下面给出一个小Demo,创建两个开关,开关一可以控制开关二的可用与否,两者的关系就相当于水闸与水龙头的关系. #import "ViewContr ...
 - STM8的GPIO驱动
			
芯片的外设一般按照这么几个流程来进行,GPIO,外部中断,定时器,串口,ADC,IIC,SPI,下面我就按照各个模式来写 首先是GPIO,STM8的GPIO拥有复用功能,这句话告诉我们必然需要配置IO ...
 - Xcode6之后创建Pch预编译文件
			
在Xcode6之前,创建一个新工程xcode会在Supporting files文件夹下面自动创建一个“工程名-Prefix.pch”文件,也是一个头文件,pch头文件的内容能被项目中的其他所有源文件 ...
 - NOIP前刷水行动
			
2016.11.15 BZOJ1009:DP+矩阵乘法+KMP BZOJ1898:矩阵乘法 BZOJ4101:贪心,考虑我们往右边撞的时候,我们会向左边冲 ,于是枚举答案点利用一个指针计算即可! 20 ...
 - html&&css 基础知识笔记
			
diV有 Class.Style.title.ID 等属性. 1.margin 空出边缘 margin:上 下 左 右(按顺时针顺序,缺少某一方向则对称) 2.border 边框(三要素:像素 形状 ...
 - redis 配置(1)
			
redis配置密码 1.通过配置文件进行配置yum方式安装的redis配置文件通常在/etc/redis.conf中,打开配置文件找到 #requirepass foobared 去掉行前的注释,并修 ...
 - phpmyadmin修改mysql数据库密码
			
甩上链接:http://jingyan.baidu.com/article/e4511cf332b9832b845eaf27.html
 - 家用计费系统ER图
			
家用计费系统研发开始.在此记录自己的开发过程.
 - CSS文字大小单位PX、EM、PT
			
老是被人问到px.pt和em的区别,自己有时候也会纠结到底该用什么单位,今天特意查了一些文章,下面这篇虽然很久远了,但解释的比较全面,转载收藏之.点击查看原文 这里引用的是Jorux的"95 ...
 - 负载均衡探测器lbd
			
负载均衡探测器lbd 大型网站为了解决海量访问问题,往往采用负载均衡技术,将用户的访问分配到不同的服务器上.网站的负载均衡可以从DNS和HTTP两个环节进行实施.在进行Web渗透测试的时候,需要先 ...