zoukankan      html  css  js  c++  java
  • 游戏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)_模态窗体管理" 继续。

  • 相关阅读:
    解析大型.NET ERP系统 权限模块设计与实现
    Enterprise Solution 开源项目资源汇总 Visual Studio Online 源代码托管 企业管理软件开发框架
    解析大型.NET ERP系统 单据编码功能实现
    解析大型.NET ERP系统 单据标准(新增,修改,删除,复制,打印)功能程序设计
    Windows 10 部署Enterprise Solution 5.5
    解析大型.NET ERP系统 设计异常处理模块
    解析大型.NET ERP系统 业务逻辑设计与实现
    解析大型.NET ERP系统 多国语言实现
    Enterprise Solution 管理软件开发框架流程实战
    解析大型.NET ERP系统 数据审计功能
  • 原文地址:https://www.cnblogs.com/LiuGuozhu/p/6476079.html
Copyright © 2011-2022 走看看