游戏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)_模态窗体管理" 继续。

时间: 2024-10-10 22:23:31

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

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

游戏UI框架设计(四) --模态窗体管理 我们在开发UI窗体时,对于"弹出窗体"往往因为需要玩家优先处理弹出小窗体,则要求玩家不能(无法)点击"父窗体",这种窗体就是典型的"模态窗体".在此笔者设计了四种模式类型:完全透明.半透明.低透明度.透明且可以穿透. (透明不能穿透) (半透明不能穿透) (低透明度,不能穿透) 对于"模态窗体"的基本实现原理是: 在弹出窗体的后面增加一层"UI遮罩窗体",当需要弹出

游戏UI框架设计(二) : 最简版本设计

最简版本设计 --最简版本设计 为降低难度决定先讲解一个最简版本,阐述UI框架的核心设计理念.这里先定义三个核心功能: 1:UI窗体的自动加载功能. 2:缓存UI窗体. 3:窗体生命周期(状态)管理. UI框架设计主要目的,就是尽可能的完成一些与具体游戏功能逻辑无关的一些底层事务性的功能实现.这些功能最好是自动或者是半自动的实现,无须客户程序(调用框架的程序)过多处理与关心. 对于以上功能,笔者定义了UI框架的相关四个核心类: BaseUIForms    基础UI窗体脚本(父类,其他窗体都继承

游戏UI框架设计(五): 配置管理与应用

游戏UI框架设计(五) --配置管理与应用 在开发企业级游戏/VR/AR产品时候,我们总是希望可以总结出一些通用的技术体系,框架结构等,为简化我们的开发起到"四两拨千金"的作用.所谓"配置管理"是指一个游戏项目(软件项目),很多需要经常变化的需求或者数据,最好以配置文件的形式存在,从而代替"硬编码"方式.      这里笔者就对游戏产品中大量应用到动态加载的情形,开发出一套通用的配置管理(脚本)工具.该工具可以很方便的对于具备"键值对&

游戏UI框架设计(五): 配置管理与应用

游戏UI框架设计(五) --配置管理与应用 在开发企业级游戏/VR/AR产品时候,我们总是希望可以总结出一些通用的技术体系,框架结构等,为简化我们的开发起到"四两拨千金"的作用.所谓"配置管理"是指一个游戏项目(软件项目),很多需要经常变化的需求或者数据,最好以配置文件的形式存在,从而代替"硬编码"方式. 这里笔者就对游戏产品中大量应用到动态加载的情形,开发出一套通用的配置管理(脚本)工具.该工具可以很方便的对于具备"键值对"

游戏UI框架设计(7): 资源国际化技术

游戏UI框架设计(7) --资源国际化技术 说起"资源国际化"技术,个人认为可以追述到微软Window2000 PC操作系统的发布,在这之前windows98操作系统的开发都是先由美国总部出一个英文版本,然后在发布windows 版本之后的大约一年后,全世界其他语言版本的操作系统才能面世. 在这一年中,就是微软驻各个国家分公司的多语言版本的翻译工作,需要从操作系统的核心到外围软件,全部翻译为所在国家语言,不留死角.       这种情况对于微软来说需要为多语言版本付出额外非常大的经济负

《开源框架那些事儿22》:UI框架设计实战

UI是User Interface的缩写,通常被认为是MVC中View的部分,作用是提供跟人机交互的可视化操作界面.MVC中Model提供内容给UI进行渲染,用户通过UI框架产生响应,一般而言会由控制层调用业务逻辑进行处理,并把处理结果以Model方式返回View,再次渲染.UI框架的大致过程就是如此,按实现方式可以分为RIA和瘦客户端方式,目前基于B/S的瘦客户端方式比较流行.UI框架套路上很简单,但是想要做好可就不容易了.目前基于MVC的框架灿若繁星,不客气的说是个软件公司就有自己的技术框架

自己动手设计并实现一个linux嵌入式UI框架(设计)

看了"自己动手设计并实现一个linux嵌入式UI框架"显然没有尽兴,因为还没有看到庐山真面目,那我今天继续,先来说说,我用到了哪些知识背景.如:C语言基础知识,尤其是指针.函数指针.内存分布,linux 基础知识.如消息队列.framebuffer.多线程.多线程同步.等,数据结构.算法(如链表.队列等),window .netframework 框架设计思想,设计模式如 mvc.观察者.单例.工厂等.知识点有点多,每个知识点有机的组合在一起,形成了一个知识网,构成了一个系统.以上提到

弱交互移动游戏服务器端框架设计

很早前即有想法设计一套稳定.高效.安全的弱交互移动网络游戏服务器端基础框架,前些天初步完成简单的初稿文档.初版设计参考了印象里以前的一些工作经历经验.这些经历经验虽已日渐模糊,但从它们这里,自己获益良多. 初稿文档暂只是简单记录了目前想到,或觉得比较重要的内容(或许会更新),具体细节等涉及较少.可能我会在业余时间里一点点实现本文所述框架,只是开发计划暂无法预期,毕竟精力很有限. 1.功能描述 1.1) 弱交互移动休闲类游戏服务器端基础框架(以房间为游戏单位,诸如棋牌类游戏) 1.2) 相较传统的

客户端热更新框架之UI热更框架设计(下)

上一篇笔者介绍了关于什么是热更新,为什么需要热更新的技术文章.本篇就专门针对UI框架的热更新功能实现部分展开讨论,讨论的重点是热更新如何与UI框架进行结合? 现在笔者把设计“UI热更新框架”的整体设计与构建过程表述如下,最后附带源码工程的下载链接,供广大学员参考研究.如果大家有什么不明白的地方,欢迎大家在评论区进行讨论.   1:首先基于笔者以前设计的SUIFW 框架,做进一步功能完善.   完善UI框架对于复杂窗体的直接转向功能.为了小伙伴们能够更好的理解本技术,文章末尾特提供了下载链接供大家