游戏UI框架设计(三)

---窗体的层级管理

  UI框架中UI窗体的“层级管理”,最核心的问题是如何进行窗体的显示管理。窗体(预设)的显示我们前面定义了三种类型: 普通、隐藏其他、反向切换。代码如下:

“普通显示”模式允许多个窗体同时显示,这种类型应用最多。例如RPG中的主城界面(见下图)。

“隐藏其他界面” 模式一般应用于全局性的窗体。我们在开发此类窗体时,为了减少UI渲染压力、提高Unity渲染效率,则设置被覆盖的窗体为“不可见”状态。(即: this.gameObject.SetActive(false))。例如一般的登录窗体、选择英雄窗体等。

  “反向切换”模式类型,一般都大量引用于“弹出窗体”中。此类窗体的特点是:显示弹出窗体时不完全覆盖底层窗体,一般在屏幕的四周会露出底层窗体。之所以命名“反向切换”是因为: 程序员要维护一种“后进先出”的“栈”的数据结构特点,即我们一般要求玩家必须先关闭弹出的顶层窗体,再依次关闭下一级窗体。如下图所示。

  上图即一种典型的弹出窗体。一般我们都要求玩家先处理弹出窗体中的信息,然后关闭此窗体。一般不允许在没有关闭子窗体的情况下,直接点击父窗体。(关于弹出窗体时,不允许玩家点击父窗体的功能实现,笔者在下节[“模态窗体管理”]一章着重讲解)。

  以上说了这么多了,我们对于“层级管理”的核心代码实现,基本都体现在“UI管理器脚本” (UIManager.cs )中。以下给出具体实现代码:

/***

*   Title: "SUIFW" 框架技术

*          主题: UI管理器

*   Description:

*          功能:整个UI框架的核心,用户程序通过调用本类,来调用本框架的大多数功能。

*          功能1:关于入“栈”与出“栈”的UI窗体4个状态的定义逻辑

*                 入栈状态:

*                     Freeze();   (上一个UI窗体)冻结

*                     Display();  (本UI窗体)显示

*                 出栈状态:

*                     Hiding();    (本UI窗体) 隐藏

*                     Redisplay(); (上一个UI窗体) 重新显示

*         功能2:增加“非栈”缓存集合。

*/

using UnityEngine;

usingUnityEngine.UI;

using System;

usingSystem.Collections.Generic;

namespace SUIFW

{

publicclassUIManager : MonoBehaviour

{

/* 字段  */

//本类实例

privatestaticUIManager_Instance = null;

//存储所有“UI窗体预设(Prefab)”路径

//参数含义: 第1个string 表示“窗体预设”名称,后一个string 表示对应的路径

privateDictionary<string,string> _DicUIFormsPaths;

//缓存所有已经打开的“UI窗体预设(Prefab)”

//参数含义: 第1个string 表示“窗体预设”名称,后一个BaseUI 表示对应的“窗体预设”

privateDictionary<string,BaseUIForms> _DicALLUIForms;

//“栈”结构表示的“当前UI窗体”集合。

privateStack<BaseUIForms>_StaCurrentUIForms;

//当前显示状态的UI窗体集合

privateDictionary<string,BaseUIForms> _DicCurrentShowUIForms;

//UI根节点

privateTransform _CanvasTransform = null;

//普通全屏界面节点

privateTransform _CanTransformNormal = null;

//固定界面节点

privateTransform _CanTransformFixed = null;

//弹出模式节点

privateTransform _CanTransformPopUp = null;

//UI脚本节点(加载各种管理脚本的节点)

privateTransform _CanTransformUIScripts = null;

///<summary>

///得到本类实例

///</summary>

///<returns></returns>

publicstaticUIManagerGetInstance()

{

if(_Instance == null)

{

_Instance = newGameObject("_UIManager").AddComponent<UIManager>();

}

return_Instance;

}

voidAwake()

{

//字段初始化

_DicUIFormsPaths = newDictionary<string,string>();

_DicALLUIForms = newDictionary<string,BaseUIForms>();

_StaCurrentUIForms = newStack<BaseUIForms>();

_DicCurrentShowUIForms = newDictionary<string,BaseUIForms>();

//初始化项目开始必须的资源加载

InitRootCanvasLoading();

//得到UI根节点、及其重要子节点

_CanvasTransform = GameObject.FindGameObjectWithTag(SysDefine.SYS_TAG_CANVAS).transform;

//得到普通全屏界面节点、固定界面节点、弹出模式节点、UI脚本节点

_CanTransformNormal = UnityHelper.FindTheChild(_CanvasTransform.gameObject,SysDefine.SYS_CANVAS_NORMAL_NODE_NAME);

_CanTransformFixed = UnityHelper.FindTheChild(_CanvasTransform.gameObject,SysDefine.SYS_CANVAS_FIXED_NODE_NAME);

_CanTransformPopUp = UnityHelper.FindTheChild(_CanvasTransform.gameObject,SysDefine.SYS_CANVAS_POPUP_NODE_NAME);

_CanTransformUIScripts = UnityHelper.FindTheChild(_CanvasTransform.gameObject,SysDefine.SYS_CANVAS_UISCRIPTS_NODE_NAME);

//把本脚本实例,作为Canvas的子节点

UnityHelper.AddChildToParent(_CanTransformUIScripts,this.gameObject.transform);

//本UI节点信息,场景转换时,不允许销毁

DontDestroyOnLoad(_CanvasTransform);

//初始化“UI窗体预设”路径数据

InitUIFormsPathsData();

}

///<summary>

///显示UI窗体

///</summary>

///<param name="strUIFormName">UI窗体的名称</param>

publicvoid ShowUIForms(stringstrUIFormName)

{

BaseUIFormsbaseUIForms;                        //UI窗体基类

//参数检查

if(string.IsNullOrEmpty(strUIFormName)) return;

//加载“UI窗体名称”,到“所有UI窗体缓存”中

baseUIForms =LoadUIFormsToAllUIFormsCatch(strUIFormName);

if(baseUIForms == null) return;

//判断是否清空“栈”结构体集合

if(baseUIForms.CurrentUIType.IsClearReverseChange)

{

ClearStackArray();

}

//判断不同的窗体显示模式,分别进行处理

switch(baseUIForms.CurrentUIType.UIForms_ShowMode)

{

caseUIFormsShowMode.Normal:

EnterUIFormsCache(strUIFormName);

break;

caseUIFormsShowMode.ReverseChange:

PushUIForms(strUIFormName);

break;

caseUIFormsShowMode.HideOther:

EnterUIFormstToCacheHideOther(strUIFormName);

break;

default:

break;

}

}

///<summary>

///关闭或返回上一个UI窗体(关闭当前UI窗体)

///</summary>

publicvoid CloseOrReturnUIForms(stringstrUIFormName)

{

BaseUIFormsbaseUIForms = null;                   //UI窗体基类

/* 参数检查 */

if(string.IsNullOrEmpty(strUIFormName)) return;

//“所有UI窗体缓存”如果没有记录,则直接返回。

_DicALLUIForms.TryGetValue(strUIFormName, outbaseUIForms);

if(baseUIForms == null) return;

/* 判断不同的窗体显示模式,分别进行处理 */

switch(baseUIForms.CurrentUIType.UIForms_ShowMode)

{

caseUIFormsShowMode.Normal:

ExitUIFormsCache(strUIFormName);

break;

caseUIFormsShowMode.ReverseChange:

PopUIForms();

break;

caseUIFormsShowMode.HideOther:

ExitUIFormsFromCacheAndShowOther(strUIFormName);

break;

default:

break;

}

}

#region私有方法

///<summary>

///根据指定UI窗体名称,加载到“所有UI窗体”缓存中。

///</summary>

///<param name="strUIFormName">UI窗体名称</param>

///<returns></returns>

privateBaseUIForms LoadUIFormsToAllUIFormsCatch(stringstrUIFormName)

{

BaseUIFormsbaseUI;                             //UI窗体

//判断“UI预设缓存集合”是否有指定的UI窗体,否则新加载窗体

_DicALLUIForms.TryGetValue(strUIFormName, outbaseUI);

if(baseUI == null)

{

//加载指定路径的“UI窗体”

baseUI =LoadUIForms(strUIFormName);

}

returnbaseUI;

}

///<summary>

///加载UI窗体到“当前显示窗体集合”缓存中。

///</summary>

///<param name="strUIFormsName"></param>

privatevoid EnterUIFormsCache(stringstrUIFormsName)

{

BaseUIFormsbaseUIForms;                        //UI窗体基类

BaseUIFormsbaseUIFormsFromAllCache;            //"所有窗体集合"中的窗体基类

//“正在显示UI窗体缓存”集合里有记录,则直接返回。

_DicCurrentShowUIForms.TryGetValue(strUIFormsName, outbaseUIForms);

if(baseUIForms != null) return;

//把当前窗体,加载到“正在显示UI窗体缓存”集合里

_DicALLUIForms.TryGetValue(strUIFormsName, outbaseUIFormsFromAllCache);

if(baseUIFormsFromAllCache != null)

{

_DicCurrentShowUIForms.Add(strUIFormsName, baseUIFormsFromAllCache);

baseUIFormsFromAllCache.Display();

}

}

///<summary>

///卸载UI窗体从“当前显示窗体集合”缓存中。

///</summary>

///<paramname="strUIFormsName"></param>

privatevoid ExitUIFormsCache(stringstrUIFormsName)

{

BaseUIFormsbaseUIForms;                        //UI窗体基类

//“正在显示UI窗体缓存”集合没有记录,则直接返回。

_DicCurrentShowUIForms.TryGetValue(strUIFormsName, outbaseUIForms);

if(baseUIForms == null) return;

//指定UI窗体,运行隐藏状态,且从“正在显示UI窗体缓存”集合中移除。

baseUIForms.Hiding();

_DicCurrentShowUIForms.Remove(strUIFormsName);

}

///<summary>

///加载UI窗体到“当前显示窗体集合”缓存中,且隐藏其他正在显示的页面

///</summary>

///<paramname="strUIFormsName"></param>

privatevoid EnterUIFormstToCacheHideOther(stringstrUIFormsName)

{

BaseUIFormsbaseUIForms;                        //UI窗体基类

BaseUIFormsbaseUIFormsFromAllCache;            //"所有窗体集合"中的窗体基类

//“正在显示UI窗体缓存”集合里有记录,则直接返回。

_DicCurrentShowUIForms.TryGetValue(strUIFormsName, outbaseUIForms);

if(baseUIForms != null) return;

//“正在显示UI窗体缓存”与“栈缓存”集合里所有窗体进行隐藏处理。

foreach(BaseUIForms baseUIFormsItem in_DicCurrentShowUIForms.Values)

{

baseUIFormsItem.Hiding();

}

foreach(BaseUIForms basUIFormsItem in_StaCurrentUIForms)

{

basUIFormsItem.Hiding();

}

//把当前窗体,加载到“正在显示UI窗体缓存”集合里

_DicALLUIForms.TryGetValue(strUIFormsName,out baseUIFormsFromAllCache);

if(baseUIFormsFromAllCache != null)

{

_DicCurrentShowUIForms.Add(strUIFormsName, baseUIFormsFromAllCache);

baseUIFormsFromAllCache.Display();

}

}

///<summary>

///卸载UI窗体从“当前显示窗体集合”缓存中,且显示其他原本需要显示的页面

///</summary>

///<paramname="strUIFormsName"></param>

privatevoidExitUIFormsFromCacheAndShowOther(stringstrUIFormsName)

{

BaseUIFormsbaseUIForms;                        //UI窗体基类

//“正在显示UI窗体缓存”集合没有记录,则直接返回。

_DicCurrentShowUIForms.TryGetValue(strUIFormsName, outbaseUIForms);

if(baseUIForms == null) return;

//指定UI窗体,运行隐藏状态,且从“正在显示UI窗体缓存”集合中移除。

baseUIForms.Hiding();

_DicCurrentShowUIForms.Remove(strUIFormsName);

//“正在显示UI窗体缓存”与“栈缓存”集合里所有窗体进行再次显示处理。

foreach(BaseUIForms baseUIFormsItem in_DicCurrentShowUIForms.Values)

{

baseUIFormsItem.Redisplay();

}

foreach(BaseUIForms basUIFormsItem in_StaCurrentUIForms)

{

basUIFormsItem.Redisplay();

}

}

///<summary>

///UI窗体入栈

///功能1: 判断栈里是否已经有窗体,有则“冻结”

///     2: 先判断“UI预设缓存集合”是否有指定的UI窗体,有则处理。

///     3: 指定UI窗体入"栈"

///</summary>

///<paramname="strUIFormsName"></param>

privatevoid PushUIForms(stringstrUIFormsName)

{

BaseUIFormsbaseUI;                             //UI预设窗体

//判断栈里是否已经有窗体,有则“冻结”

if(_StaCurrentUIForms.Count > 0)

{

BaseUIFormstopUIForms = _StaCurrentUIForms.Peek();

topUIForms.Freeze();

}

//先判断“UI预设缓存集合”是否有指定的UI窗体,有则处理。

_DicALLUIForms.TryGetValue(strUIFormsName, outbaseUI);

if(baseUI != null)

{

baseUI.Display();

}

else

{

Log.Write(GetType()+ string.Format("/PushUIForms()/ baseUI==null! 核心错误,请检查strUIFormsName={0}", strUIFormsName), Log.Level.High);

}

//指定UI窗体入"栈"

_StaCurrentUIForms.Push(baseUI);

}

///<summary>

///UI窗体出栈逻辑

///</summary>

privatevoid PopUIForms()

{

if(_StaCurrentUIForms.Count >= 2)

{

/* 出栈逻辑 */

BaseUIFormstopUIForms = _StaCurrentUIForms.Pop();

//出栈的窗体,进行隐藏处理

topUIForms.Hiding();

//出栈窗体的下一个窗体逻辑

BaseUIFormsnextUIForms = _StaCurrentUIForms.Peek();

//下一个窗体"重新显示"处理

nextUIForms.Redisplay();

}

elseif (_StaCurrentUIForms.Count == 1)

{

/* 出栈逻辑 */

BaseUIFormstopUIForms = _StaCurrentUIForms.Pop();

//出栈的窗体,进行"隐藏"处理

topUIForms.Hiding();

}

}

///<summary>

///加载与显示UI窗体

///功能:

///    1:根据“UI窗体预设”名称,加载预设克隆体。

///    2:预设克隆体添加UI“根节点”为父节点。

///    3:隐藏刚创建的UI克隆体。

///    4:新创建的“UI窗体”,加入“UI窗体缓存”中

///</summary>

privateBaseUIForms LoadUIForms(stringstrUIFormsName)

{

stringstrUIFormsPaths = null;                  //UI窗体的路径

GameObjectgoCloneUIPrefab = null;              //克隆的"窗体预设"

BaseUIFormsbaseUIForm;                         //UI窗体

//得到UI窗体的路径

_DicUIFormsPaths.TryGetValue(strUIFormsName, outstrUIFormsPaths);

//加载指定路径的“UI窗体”

if(!string.IsNullOrEmpty(strUIFormsPaths))

{

goCloneUIPrefab = ResourcesMgr.GetInstance().LoadAsset(strUIFormsPaths,false);

}

//设置“UI窗体”克隆体的父节点,以及隐藏处理与加入“UI窗体缓存”中

if(_CanvasTransform != null&& goCloneUIPrefab != null)

{

baseUIForm = goCloneUIPrefab.GetComponent<BaseUIForms>();

if(baseUIForm == null)

{

Log.Write(GetType()+ string.Format("/LoadUIForms()/ baseUIForm==null!,请先确认克隆对象上是否加载了BaseUIForms的子类。参数 strUIFormsName='{0}' ", strUIFormsName), Log.Level.High);

returnnull;

}

switch(baseUIForm.CurrentUIType.UIForms_Type)

{

caseUIFormsType.Normal:

goCloneUIPrefab.transform.SetParent(_CanTransformNormal,false);

break;

caseUIFormsType.Fixed:

goCloneUIPrefab.transform.SetParent(_CanTransformFixed, false);

break;

caseUIFormsType.PopUp:

goCloneUIPrefab.transform.SetParent(_CanTransformPopUp, false);

break;

default:

break;

}

goCloneUIPrefab.SetActive(false);

//新创建的“UI窗体”,加入“UI窗体缓存”中

_DicALLUIForms.Add(strUIFormsName, baseUIForm);

returnbaseUIForm;

}

else

{

Log.Write(GetType()+ string.Format("/LoadUIForms()/‘_CanvasTransform’ Or ‘goCloneUIPrefab’==NULL!  , 方法参数strUIFormsName={0},请检查!", strUIFormsName), Log.Level.High);

}

Log.Write(GetType()+ string.Format("/LoadUIForms()/ 出现不可预知错误,请检查! 方法参数strUIFormsName={0}", strUIFormsName), Log.Level.High);

returnnull;

}

///<summary>

///初始化项目开始必须的资源加载

///</summary>

privatevoid InitRootCanvasLoading()

{

if(UnityHelper.isFirstLoad)

{

ResourcesMgr.GetInstance().LoadAsset(SysDefine.SYS_PATH_CANVAS, false);

}

}

///<summary>

///初始化“UI窗体预设”路径数据

///</summary>

privatevoid InitUIFormsPathsData()

{

//测试也成功

IConfigManagerconfigMgr = newConfigManagerByJson(SysDefine.SYS_PATH_UIFormConfigJson);

if(_DicUIFormsPaths != null)

{

_DicUIFormsPaths =configMgr.AppSetting;

}

}

///<summary>

///清空“栈”结构体集合

///</summary>

///<returns></returns>

privatebool ClearStackArray()

{

if(_StaCurrentUIForms != null&& _StaCurrentUIForms.Count >= 1)

{

_StaCurrentUIForms.Clear();

returntrue;

}

returnfalse;

}

#endregion

}//Class_end

}

以上代码解释:

1: UIManager.cs  中定义的新的字段 ,“_StaCurrentUIForms” 就是一个“栈”数据类型,用于维护一种后进先出的数据结构。常见的方法如下:

   C#语言中提供 Stack<T> 泛型集合,来直接实现这种结构。
常用属性与方法:

  •  Count 属性  查询栈内元素数量

  •  Push()      压栈

  •  Pop()       出栈

  •  Peek()      查询栈顶元素

  •  GetEnumerator() 遍历栈中所有元素

2: UIManager.cs 中的“ShowUIForms()”方法中的PushUIForms()与EnterUIFormstToCacheHideOther() 方法,就是专门处理“反向切换”与“隐藏其他”窗体特性的实现方法。

好了时间不早了就先写到这吧,大家有什么疑问可以讨论,这里笔者也主要是想起到“抛砖引玉”的作用。

本篇就先写到这,下篇 "游戏UI框架设计(4)_模态窗体管理" 继续。

转载于:https://blog.51cto.com/liuguozhu/1901811

游戏UI框架设计(三) : 窗体的层级管理相关推荐

  1. 游戏UI框架设计(四) : 模态窗体管理

    我们在开发UI窗体时,对于"弹出窗体"往往因为需要玩家优先处理弹出小窗体,则要求玩家不能(无法)点击"父窗体",这种窗体就是典型的"模态窗体" ...

  2. 基于UGUI 的 游戏UI框架的一些想法

    UI 框架可以很简单,具有打开某些窗口,关闭某些窗口的功能. 也可以很复杂,具有动画效果,层级管理,灰度管理,各种bar配置,链式打开模式,消息窗口 等等. 而作为一个设计人员,最主要的是要使得你的设 ...

  3. Vue CLI 3结合Lerna进行UI框架设计

    第一次在掘金发文章,有点啰里啰嗦,大家见谅. 当前大部分UI框架设计的Webpack配置都相对复杂,例如Element.Ant Design Vue和Muse-UI等Vue组件库.例如Element, ...

  4. 为什么ui框架设计成单线程_评估UI设计的备忘单

    为什么ui框架设计成单线程 Whether you're evaluating your design proposals or giving feedback to a colleague duri ...

  5. 游戏UI界面设计视频教程全套

    库(UI插画漫画开发建模美术教程大全) 2014-11-28 16:54:21 1.全面系统(从基础到进阶,涵盖所有细节,特别是没有受过特别专业培训的亲们,更加需要) 2.有序排列(按照教程属性与绘画 ...

  6. 包含对象名字的游戏id_教你从头写游戏服务器框架(三)

    关于作者:韩伟,腾讯互娱高级工程师,目前在Next产品中心研发创新类型游戏. 本文为系列文章的第 3篇 第一篇:教你从头写游戏服务器框架(1) 第二篇:教你从头写游戏服务器框架(2)   协  程   ...

  7. ui动效 unity_针对Unity3D的小游戏UI框架——IceCreamView

    前言 IceCreamView是什么? IceCreamView(以下简称ICView)是一套针对Unity3D的小游戏UI快速构建框架. 同时ICView是一套面向组件开发模式的UI框架,以容器+组 ...

  8. ExtJs UI框架学习三

    JavaScript 面向对象及设计模式系列--灵活的JavaScript,Timo.Lee 当前,Javascript已经成为世界上最受欢迎和被广泛应用的的编程语言--因为他被捆绑到各种浏览器中.作 ...

  9. RXJava2响应式编程框架设计三---Rxjava2背压、生命周期

    在上一次https://www.cnblogs.com/webor2006/p/12348890.html中已经完成了对RxJava2的整个线程切换原理的详细剖析了,这次继续来学习它其它比较重要的知识 ...

最新文章

  1. 文本编辑器实现复制、剪切、粘贴、撤销、重做操作
  2. 数据结构与算法 / 平衡二叉树(AVL树)
  3. java如何调用系统保存框_java使用poi实现excel导出之后如何弹出保存提示框
  4. 【每日SQL打卡】​​​​​​​​​​​​​​​DAY 16丨报告的记录 II【难度中等】
  5. c语言加速度积分得到速度_自编微积分教材-第一章 微积分漫谈(1)
  6. 从敲下一行JS代码到这行代码被执行,中间发生了什么?
  7. rk3288 android5.1 java 层使用 su 获取 root 权限
  8. Unity OnPostprocessTexture 和 OnPreprocessTexture 使用注意事项
  9. Java删除文件和文件夹的方式
  10. 根据字体图片,查找下载Font字体
  11. 软件测试面试题(等待解答)
  12. vue全局混入minx
  13. yolov4与yolov5的区别
  14. 8大底层逻辑,提升思维能力
  15. RuntimeError: CUDA error: an illegal memory access was encountered
  16. 九章算术 八:《方程》
  17. 《神经科学:探索脑》学习笔记(第13章 运动的脊髓控制)
  18. input输入框限制中文汉字只能输入20个字符,英文10个字符!
  19. Android 如何清空 Canvas 清屏只需三句话
  20. 微信企业号开发中文乱码问题

热门文章

  1. tcp reno 介绍
  2. HDU 2444 The Accomodation of Students (二部图+染色)
  3. JAVA对象通过jackson转成json格式,属性名首字母变成小写的解决方法
  4. 基于FPGA的以太网开发
  5. Lync Server 2013 实战系列之六:标准版-安装和更新LyncServer 系统
  6. C++资源之不完全导引
  7. python os.path模块学习(转)
  8. Mysql HA实现MYSQL的高可用
  9. 拖放操作和文件复制小功能
  10. 网络上一个人也是孤独地.