游戏UI框架设计(三) : 窗体的层级管理
游戏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框架设计(三) : 窗体的层级管理相关推荐
- 游戏UI框架设计(四) : 模态窗体管理
我们在开发UI窗体时,对于"弹出窗体"往往因为需要玩家优先处理弹出小窗体,则要求玩家不能(无法)点击"父窗体",这种窗体就是典型的"模态窗体" ...
- 基于UGUI 的 游戏UI框架的一些想法
UI 框架可以很简单,具有打开某些窗口,关闭某些窗口的功能. 也可以很复杂,具有动画效果,层级管理,灰度管理,各种bar配置,链式打开模式,消息窗口 等等. 而作为一个设计人员,最主要的是要使得你的设 ...
- Vue CLI 3结合Lerna进行UI框架设计
第一次在掘金发文章,有点啰里啰嗦,大家见谅. 当前大部分UI框架设计的Webpack配置都相对复杂,例如Element.Ant Design Vue和Muse-UI等Vue组件库.例如Element, ...
- 为什么ui框架设计成单线程_评估UI设计的备忘单
为什么ui框架设计成单线程 Whether you're evaluating your design proposals or giving feedback to a colleague duri ...
- 游戏UI界面设计视频教程全套
库(UI插画漫画开发建模美术教程大全) 2014-11-28 16:54:21 1.全面系统(从基础到进阶,涵盖所有细节,特别是没有受过特别专业培训的亲们,更加需要) 2.有序排列(按照教程属性与绘画 ...
- 包含对象名字的游戏id_教你从头写游戏服务器框架(三)
关于作者:韩伟,腾讯互娱高级工程师,目前在Next产品中心研发创新类型游戏. 本文为系列文章的第 3篇 第一篇:教你从头写游戏服务器框架(1) 第二篇:教你从头写游戏服务器框架(2) 协 程 ...
- ui动效 unity_针对Unity3D的小游戏UI框架——IceCreamView
前言 IceCreamView是什么? IceCreamView(以下简称ICView)是一套针对Unity3D的小游戏UI快速构建框架. 同时ICView是一套面向组件开发模式的UI框架,以容器+组 ...
- ExtJs UI框架学习三
JavaScript 面向对象及设计模式系列--灵活的JavaScript,Timo.Lee 当前,Javascript已经成为世界上最受欢迎和被广泛应用的的编程语言--因为他被捆绑到各种浏览器中.作 ...
- RXJava2响应式编程框架设计三---Rxjava2背压、生命周期
在上一次https://www.cnblogs.com/webor2006/p/12348890.html中已经完成了对RxJava2的整个线程切换原理的详细剖析了,这次继续来学习它其它比较重要的知识 ...
最新文章
- 文本编辑器实现复制、剪切、粘贴、撤销、重做操作
- 数据结构与算法 / 平衡二叉树(AVL树)
- java如何调用系统保存框_java使用poi实现excel导出之后如何弹出保存提示框
- 【每日SQL打卡】​​​​​​​​​​​​​​​DAY 16丨报告的记录 II【难度中等】
- c语言加速度积分得到速度_自编微积分教材-第一章 微积分漫谈(1)
- 从敲下一行JS代码到这行代码被执行,中间发生了什么?
- rk3288 android5.1 java 层使用 su 获取 root 权限
- Unity OnPostprocessTexture 和 OnPreprocessTexture 使用注意事项
- Java删除文件和文件夹的方式
- 根据字体图片,查找下载Font字体
- 软件测试面试题(等待解答)
- vue全局混入minx
- yolov4与yolov5的区别
- 8大底层逻辑,提升思维能力
- RuntimeError: CUDA error: an illegal memory access was encountered
- 九章算术 八:《方程》
- 《神经科学:探索脑》学习笔记(第13章 运动的脊髓控制)
- input输入框限制中文汉字只能输入20个字符,英文10个字符!
- Android 如何清空 Canvas 清屏只需三句话
- 微信企业号开发中文乱码问题