游戲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)_模態窗體管理" 繼續。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM