首页 > 代码库 > 游戏UI框架设计(三) : 窗体的层级管理

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


本文出自 “刘老师讲Unity” 博客,请务必保留此出处http://liuguozhu.blog.51cto.com/9142031/1901811

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