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


    游戏UI框架设计(三)

    ---窗体的层级管理

     

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

        /// <summary>
        /// UI窗体显示类型
        /// </summary>
        public enum UIFormsShowMode
        {
            Normal,             //普通显示
            ReverseChange,      //反向切换      
            HideOther,          //隐藏其他界面 
        }

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

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

      

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

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

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

      1 /***
      2  *    Title: "SUIFW" 框架
      3  *           主题: UI管理器     
      4  *    Description: 
      5  *           功能:整个UI框架的核心,用户程序通过调用本类,来调用本框架的大多数功能。  
      6  *           功能1:关于入“栈”与出“栈”的UI窗体4个状态的定义逻辑
      7  *                 入栈状态:
      8  *                     Freeze();   (上一个UI窗体)冻结
      9  *                     Display();  (本UI窗体)显示
     10  *                 出栈状态: 
     11  *                     Hiding();    (本UI窗体) 隐藏
     12  *                     Redisplay(); (上一个UI窗体) 重新显示
     13  *          功能2:增加“非栈”缓存集合。 
     14  */
     15 using UnityEngine;
     16 using UnityEngine.UI;
     17 using System;
     18 using System.Collections.Generic;
     19 
     20 
     21 namespace SUIFW
     22 {
     23     public class UIManager : MonoBehaviour
     24     {
     25         /* 字段  */
     26         //本类实例
     27         private static UIManager _Instance = null;
     28         //存储所有“UI窗体预设(Prefab)”路径
     29         //参数含义: 第1个string 表示“窗体预设”名称,后一个string 表示对应的路径
     30         private Dictionary<string, string> _DicUIFormsPaths;
     31         //缓存所有已经打开的“UI窗体预设(Prefab)”
     32         //参数含义: 第1个string 表示“窗体预设”名称,后一个BaseUI 表示对应的“窗体预设”
     33         private Dictionary<string, BaseUIForms> _DicALLUIForms;
     34         //“栈”结构表示的“当前UI窗体”集合。
     35         private Stack<BaseUIForms> _StaCurrentUIForms;
     36         //当前显示状态的UI窗体集合
     37         private Dictionary<string, BaseUIForms> _DicCurrentShowUIForms;
     38         //UI根节点
     39         private Transform _CanvasTransform = null;
     40         //普通全屏界面节点
     41         private Transform _CanTransformNormal = null;
     42         //固定界面节点
     43         private Transform _CanTransformFixed = null;
     44         //弹出模式节点
     45         private Transform _CanTransformPopUp = null;
     46         //UI脚本节点(加载各种管理脚本的节点)
     47         private Transform _CanTransformUIScripts = null;
     48 
     49 
     50 
     51 
     52         /// <summary>
     53         /// 得到本类实例
     54         /// </summary>
     55         /// <returns></returns>
     56         public static UIManager GetInstance()
     57         {
     58             if (_Instance == null)
     59             {
     60                 _Instance = new GameObject("_UIManager").AddComponent<UIManager>();
     61             }
     62             return _Instance;
     63         }
     64 
     65         void Awake()
     66         {
     67             //字段初始化
     68             _DicUIFormsPaths = new Dictionary<string, string>();
     69             _DicALLUIForms = new Dictionary<string, BaseUIForms>();
     70             _StaCurrentUIForms = new Stack<BaseUIForms>();
     71             _DicCurrentShowUIForms = new Dictionary<string, BaseUIForms>();
     72 
     73             //初始化项目开始必须的资源加载
     74             InitRootCanvasLoading();
     75 
     76             //得到UI根节点、及其重要子节点                     
     77             _CanvasTransform = GameObject.FindGameObjectWithTag(SysDefine.SYS_TAG_CANVAS).transform;
     78             //得到普通全屏界面节点、固定界面节点、弹出模式节点、UI脚本节点
     79             _CanTransformNormal = UnityHelper.FindTheChild(_CanvasTransform.gameObject, SysDefine.SYS_CANVAS_NORMAL_NODE_NAME);
     80             _CanTransformFixed = UnityHelper.FindTheChild(_CanvasTransform.gameObject, SysDefine.SYS_CANVAS_FIXED_NODE_NAME);
     81             _CanTransformPopUp = UnityHelper.FindTheChild(_CanvasTransform.gameObject, SysDefine.SYS_CANVAS_POPUP_NODE_NAME);
     82             _CanTransformUIScripts = UnityHelper.FindTheChild(_CanvasTransform.gameObject, SysDefine.SYS_CANVAS_UISCRIPTS_NODE_NAME);
     83 
     84             //把本脚本实例,作为Canvas的子节点
     85             UnityHelper.AddChildToParent(_CanTransformUIScripts, this.gameObject.transform);
     86 
     87             //本UI节点信息,场景转换时,不允许销毁
     88             DontDestroyOnLoad(_CanvasTransform);
     89             //初始化“UI窗体预设”路径数据
     90             InitUIFormsPathsData();
     91         }
     92 
     93         /// <summary>
     94         /// 显示UI窗体
     95         /// </summary>
     96         /// <param name="strUIFormName">UI窗体的名称</param>
     97         public void ShowUIForms(string strUIFormName)
     98         {
     99             BaseUIForms baseUIForms;                        //UI窗体基类
    100 
    101             //参数检查
    102             if (string.IsNullOrEmpty(strUIFormName)) return;
    103 
    104             //加载“UI窗体名称”,到“所有UI窗体缓存”中
    105             baseUIForms = LoadUIFormsToAllUIFormsCatch(strUIFormName);
    106             if (baseUIForms == null) return;
    107 
    108             //判断是否清空“栈”结构体集合
    109             if (baseUIForms.CurrentUIType.IsClearReverseChange)
    110             {
    111                 ClearStackArray();
    112             }
    113 
    114             //判断不同的窗体显示模式,分别进行处理
    115             switch (baseUIForms.CurrentUIType.UIForms_ShowMode)
    116             {
    117                 case UIFormsShowMode.Normal:
    118                     EnterUIFormsCache(strUIFormName);
    119                     break;
    120                 case UIFormsShowMode.ReverseChange:
    121                     PushUIForms(strUIFormName);
    122                     break;
    123                 case UIFormsShowMode.HideOther:
    124                     EnterUIFormstToCacheHideOther(strUIFormName);
    125                     break;
    126                 default:
    127                     break;
    128             }
    129         }
    130 
    131         /// <summary>
    132         /// 关闭或返回上一个UI窗体(关闭当前UI窗体)
    133         /// </summary>
    134         public void CloseOrReturnUIForms(string strUIFormName)
    135         {
    136             BaseUIForms baseUIForms = null;                   //UI窗体基类
    137 
    138             /* 参数检查 */
    139             if (string.IsNullOrEmpty(strUIFormName)) return;
    140             //“所有UI窗体缓存”如果没有记录,则直接返回。
    141             _DicALLUIForms.TryGetValue(strUIFormName, out baseUIForms);
    142             if (baseUIForms == null) return;
    143 
    144             /* 判断不同的窗体显示模式,分别进行处理 */
    145             switch (baseUIForms.CurrentUIType.UIForms_ShowMode)
    146             {
    147                 case UIFormsShowMode.Normal:
    148                     ExitUIFormsCache(strUIFormName);
    149                     break;
    150                 case UIFormsShowMode.ReverseChange:
    151                     PopUIForms();
    152                     break;
    153                 case UIFormsShowMode.HideOther:
    154                     ExitUIFormsFromCacheAndShowOther(strUIFormName);
    155                     break;
    156                 default:
    157                     break;
    158             }
    159 
    160         }
    161 
    162         #region 私有方法
    163         /// <summary>
    164         /// 根据指定UI窗体名称,加载到“所有UI窗体”缓存中。
    165         /// </summary>
    166         /// <param name="strUIFormName">UI窗体名称</param>
    167         /// <returns></returns>
    168         private BaseUIForms LoadUIFormsToAllUIFormsCatch(string strUIFormName)
    169         {
    170             BaseUIForms baseUI;                             //UI窗体
    171 
    172             //判断“UI预设缓存集合”是否有指定的UI窗体,否则新加载窗体
    173             _DicALLUIForms.TryGetValue(strUIFormName, out baseUI);
    174             if (baseUI == null)
    175             {
    176                 //加载指定路径的“UI窗体”
    177                 baseUI = LoadUIForms(strUIFormName);
    178             }
    179 
    180             return baseUI;
    181         }
    182 
    183         /// <summary>
    184         /// 加载UI窗体到“当前显示窗体集合”缓存中。
    185         /// </summary>
    186         /// <param name="strUIFormsName"></param>
    187         private void EnterUIFormsCache(string strUIFormsName)
    188         {
    189             BaseUIForms baseUIForms;                        //UI窗体基类
    190             BaseUIForms baseUIFormsFromAllCache;            //"所有窗体集合"中的窗体基类
    191 
    192             //“正在显示UI窗体缓存”集合里有记录,则直接返回。
    193             _DicCurrentShowUIForms.TryGetValue(strUIFormsName, out baseUIForms);
    194             if (baseUIForms != null) return;
    195 
    196             //把当前窗体,加载到“正在显示UI窗体缓存”集合里
    197             _DicALLUIForms.TryGetValue(strUIFormsName, out baseUIFormsFromAllCache);
    198             if (baseUIFormsFromAllCache != null)
    199             {
    200                 _DicCurrentShowUIForms.Add(strUIFormsName, baseUIFormsFromAllCache);
    201                 baseUIFormsFromAllCache.Display();
    202             }
    203         }
    204 
    205         /// <summary>
    206         /// 卸载UI窗体从“当前显示窗体集合”缓存中。
    207         /// </summary>
    208         /// <param name="strUIFormsName"></param>
    209         private void ExitUIFormsCache(string strUIFormsName)
    210         {
    211             BaseUIForms baseUIForms;                        //UI窗体基类
    212 
    213             //“正在显示UI窗体缓存”集合没有记录,则直接返回。
    214             _DicCurrentShowUIForms.TryGetValue(strUIFormsName, out baseUIForms);
    215             if (baseUIForms == null) return;
    216 
    217             //指定UI窗体,运行隐藏状态,且从“正在显示UI窗体缓存”集合中移除。
    218             baseUIForms.Hiding();
    219             _DicCurrentShowUIForms.Remove(strUIFormsName);
    220         }
    221 
    222         /// <summary>
    223         /// 加载UI窗体到“当前显示窗体集合”缓存中,且隐藏其他正在显示的页面
    224         /// </summary>
    225         /// <param name="strUIFormsName"></param>
    226         private void EnterUIFormstToCacheHideOther(string strUIFormsName)
    227         {
    228             BaseUIForms baseUIForms;                        //UI窗体基类
    229             BaseUIForms baseUIFormsFromAllCache;            //"所有窗体集合"中的窗体基类
    230 
    231             //“正在显示UI窗体缓存”集合里有记录,则直接返回。
    232             _DicCurrentShowUIForms.TryGetValue(strUIFormsName, out baseUIForms);
    233             if (baseUIForms != null) return;
    234 
    235             //“正在显示UI窗体缓存”与“栈缓存”集合里所有窗体进行隐藏处理。
    236             foreach (BaseUIForms baseUIFormsItem in _DicCurrentShowUIForms.Values)
    237             {
    238                 baseUIFormsItem.Hiding();
    239             }
    240             foreach (BaseUIForms basUIFormsItem in _StaCurrentUIForms)
    241             {
    242                 basUIFormsItem.Hiding();
    243             }
    244 
    245             //把当前窗体,加载到“正在显示UI窗体缓存”集合里
    246             _DicALLUIForms.TryGetValue(strUIFormsName, out baseUIFormsFromAllCache);
    247             if (baseUIFormsFromAllCache != null)
    248             {
    249                 _DicCurrentShowUIForms.Add(strUIFormsName, baseUIFormsFromAllCache);
    250                 baseUIFormsFromAllCache.Display();
    251             }
    252         }
    253 
    254         /// <summary>
    255         /// 卸载UI窗体从“当前显示窗体集合”缓存中,且显示其他原本需要显示的页面
    256         /// </summary>
    257         /// <param name="strUIFormsName"></param>
    258         private void ExitUIFormsFromCacheAndShowOther(string strUIFormsName)
    259         {
    260             BaseUIForms baseUIForms;                        //UI窗体基类
    261 
    262             //“正在显示UI窗体缓存”集合没有记录,则直接返回。
    263             _DicCurrentShowUIForms.TryGetValue(strUIFormsName, out baseUIForms);
    264             if (baseUIForms == null) return;
    265 
    266             //指定UI窗体,运行隐藏状态,且从“正在显示UI窗体缓存”集合中移除。
    267             baseUIForms.Hiding();
    268             _DicCurrentShowUIForms.Remove(strUIFormsName);
    269 
    270             //“正在显示UI窗体缓存”与“栈缓存”集合里所有窗体进行再次显示处理。
    271             foreach (BaseUIForms baseUIFormsItem in _DicCurrentShowUIForms.Values)
    272             {
    273                 baseUIFormsItem.Redisplay();
    274             }
    275             foreach (BaseUIForms basUIFormsItem in _StaCurrentUIForms)
    276             {
    277                 basUIFormsItem.Redisplay();
    278             }
    279         }
    280 
    281         /// <summary>
    282         /// UI窗体入栈
    283         /// 功能1: 判断栈里是否已经有窗体,有则“冻结”
    284         ///     2: 先判断“UI预设缓存集合”是否有指定的UI窗体,有则处理。
    285         ///     3: 指定UI窗体入"栈"
    286         /// </summary>
    287         /// <param name="strUIFormsName"></param>
    288         private void PushUIForms(string strUIFormsName)
    289         {
    290             BaseUIForms baseUI;                             //UI预设窗体
    291 
    292 
    293             //判断栈里是否已经有窗体,有则“冻结”
    294             if (_StaCurrentUIForms.Count > 0)
    295             {
    296                 BaseUIForms topUIForms = _StaCurrentUIForms.Peek();
    297                 topUIForms.Freeze();
    298             }
    299 
    300             //先判断“UI预设缓存集合”是否有指定的UI窗体,有则处理。
    301             _DicALLUIForms.TryGetValue(strUIFormsName, out baseUI);
    302             if (baseUI != null)
    303             {
    304                 baseUI.Display();
    305             }
    306             else
    307             {
    308                 Log.Write(GetType() + string.Format("/PushUIForms()/ baseUI==null! 核心错误,请检查 strUIFormsName={0} ", strUIFormsName), Log.Level.High);
    309             }
    310 
    311             //指定UI窗体入"栈"
    312             _StaCurrentUIForms.Push(baseUI);
    313         }
    314 
    315         /// <summary>
    316         /// UI窗体出栈逻辑
    317         /// </summary>
    318         private void PopUIForms()
    319         {
    320             if (_StaCurrentUIForms.Count >= 2)
    321             {
    322                 /* 出栈逻辑 */
    323                 BaseUIForms topUIForms = _StaCurrentUIForms.Pop();
    324                 //出栈的窗体,进行隐藏处理
    325                 topUIForms.Hiding();
    326                 //出栈窗体的下一个窗体逻辑
    327                 BaseUIForms nextUIForms = _StaCurrentUIForms.Peek();
    328                 //下一个窗体"重新显示"处理
    329                 nextUIForms.Redisplay();
    330             }
    331             else if (_StaCurrentUIForms.Count == 1)
    332             {
    333                 /* 出栈逻辑 */
    334                 BaseUIForms topUIForms = _StaCurrentUIForms.Pop();
    335                 //出栈的窗体,进行"隐藏"处理
    336                 topUIForms.Hiding();
    337             }
    338         }
    339 
    340         /// <summary>
    341         /// 加载与显示UI窗体
    342         /// 功能:
    343         ///    1:根据“UI窗体预设”名称,加载预设克隆体。
    344         ///    2:预设克隆体添加UI“根节点”为父节点。
    345         ///    3:隐藏刚创建的UI克隆体。
    346         ///    4:新创建的“UI窗体”,加入“UI窗体缓存”中
    347         /// </summary>
    348         private BaseUIForms LoadUIForms(string strUIFormsName)
    349         {
    350             string strUIFormsPaths = null;                  //UI窗体的路径
    351             GameObject goCloneUIPrefab = null;              //克隆的"窗体预设"
    352             BaseUIForms baseUIForm;                         //UI窗体
    353 
    354 
    355             //得到UI窗体的路径
    356             _DicUIFormsPaths.TryGetValue(strUIFormsName, out strUIFormsPaths);
    357 
    358             //加载指定路径的“UI窗体”
    359             if (!string.IsNullOrEmpty(strUIFormsPaths))
    360             {
    361                 goCloneUIPrefab = ResourcesMgr.GetInstance().LoadAsset(strUIFormsPaths, false);
    362             }
    363 
    364             //设置“UI窗体”克隆体的父节点,以及隐藏处理与加入“UI窗体缓存”中
    365             if (_CanvasTransform != null && goCloneUIPrefab != null)
    366             {
    367                 baseUIForm = goCloneUIPrefab.GetComponent<BaseUIForms>();
    368                 if (baseUIForm == null)
    369                 {
    370                     Log.Write(GetType() + string.Format("/LoadUIForms()/ baseUIForm==null!,请先确认克隆对象上是否加载了BaseUIForms的子类。参数 strUIFormsName='{0}' ", strUIFormsName), Log.Level.High);
    371                     return null;
    372                 }
    373                 switch (baseUIForm.CurrentUIType.UIForms_Type)
    374                 {
    375                     case UIFormsType.Normal:
    376                         goCloneUIPrefab.transform.SetParent(_CanTransformNormal, false);
    377                         break;
    378                     case UIFormsType.Fixed:
    379                         goCloneUIPrefab.transform.SetParent(_CanTransformFixed, false);
    380                         break;
    381                     case UIFormsType.PopUp:
    382                         goCloneUIPrefab.transform.SetParent(_CanTransformPopUp, false);
    383                         break;
    384                     default:
    385                         break;
    386                 }
    387 
    388                 goCloneUIPrefab.SetActive(false);
    389                 //新创建的“UI窗体”,加入“UI窗体缓存”中
    390                 _DicALLUIForms.Add(strUIFormsName, baseUIForm);
    391                 return baseUIForm;
    392             }
    393             else
    394             {
    395                 Log.Write(GetType() + string.Format("/LoadUIForms()/‘_CanvasTransform’ Or ‘goCloneUIPrefab’==NULL!  , 方法参数 strUIFormsName={0},请检查!", strUIFormsName), Log.Level.High);
    396             }
    397 
    398             Log.Write(GetType() + string.Format("/LoadUIForms()/ 出现不可预知错误,请检查! 方法参数 strUIFormsName={0} ", strUIFormsName), Log.Level.High);
    399             return null;
    400         }
    401 
    402         /// <summary>
    403         /// 初始化项目开始必须的资源加载
    404         /// </summary>
    405         private void InitRootCanvasLoading()
    406         {
    407             if (UnityHelper.isFirstLoad)
    408             {
    409                 ResourcesMgr.GetInstance().LoadAsset(SysDefine.SYS_PATH_CANVAS, false);
    410             }
    411         }
    412 
    413         /// <summary>
    414         /// 初始化“UI窗体预设”路径数据
    415         /// </summary>
    416         private void InitUIFormsPathsData()
    417         {
    418             //测试也成功
    419             IConfigManager configMgr = new ConfigManagerByJson(SysDefine.SYS_PATH_UIFormConfigJson);
    420             if (_DicUIFormsPaths != null)
    421             {
    422                 _DicUIFormsPaths = configMgr.AppSetting;
    423             }
    424         }
    425 
    426         /// <summary>
    427         /// 清空“栈”结构体集合
    428         /// </summary>
    429         /// <returns></returns>
    430         private bool ClearStackArray()
    431         {
    432             if (_StaCurrentUIForms != null && _StaCurrentUIForms.Count >= 1)
    433             {
    434                 _StaCurrentUIForms.Clear();
    435                 return true;
    436             }
    437             return false;
    438         }
    439 
    440         #endregion
    441 
    442     }//Class_end
    443 }

    以上代码解释:

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

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

    •  Count 属性  查询栈内元素数量
    •  Push()      压栈
    •  Pop()       出栈
    •  Peek()      查询栈顶元素
    •  GetEnumerator() 遍历栈中所有元素

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

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

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

  • 相关阅读:
    字符串hash
    堆优化的最短路
    unordered_map 的火车头
    扩展欧几里得求ax+by=c的最小正整数解
    欧拉筛
    Codeforces Round 649 (Rated for Div. 2)D. Ehab s Last Corollary (图论+简单环)
    牛客SQL题解-找出所有员工具体的薪水salary情况
    牛客SQL题解-查找薪水变动超过15次的员工号emp_no以及其对应的变动次数t
    牛客SQL题解-查找所有已经分配部门的员工的last_name和first_name以及dept_no,也包括暂时没有分配具体部门的员工
    牛客SQL题解- 查找所有已经分配部门的员工的last_name和first_name
  • 原文地址:https://www.cnblogs.com/LiuGuozhu/p/6476079.html
Copyright © 2020-2023  润新知