遊戲UI框架設計(三)
程序員
---窗體的層級管理緩存
UI框架中UI窗體的「層級管理」,最核心的問題是如何進行窗體的顯示管理。窗體(預設)的顯示咱們前面定義了三種類型: 普通、隱藏其餘、反向切換。代碼以下:數據結構
「普通顯示」模式容許多個窗體同時顯示,這種類型應用最多。例如RPG中的主城界面(見下圖)。框架
「隱藏其餘界面」 模式通常應用於全局性的窗體。咱們在開發此類窗體時,爲了減小UI渲染壓力、提升Unity渲染效率,則設置被覆蓋的窗體爲「不可見」狀態。(即: this.gameObject.SetActive(false))。例如通常的登陸窗體、選擇英雄窗體等。ide
「反向切換」模式類型,通常都大量引用於「彈出窗體」中。此類窗體的特色是:顯示彈出窗體時不徹底覆蓋底層窗體,通常在屏幕的四周會露出底層窗體。之因此命名「反向切換」是由於: 程序員要維護一種「後進先出」的「棧」的數據結構特色,即咱們通常要求玩家必須先關閉彈出的頂層窗體,再依次關閉下一級窗體。以下圖所示。測試
上圖即一種典型的彈出窗體。通常咱們都要求玩家先處理彈出窗體中的信息,而後關閉此窗體。通常不容許在沒有關閉子窗體的狀況下,直接點擊父窗體。(關於彈出窗體時,不容許玩家點擊父窗體的功能實現,筆者在下節[「模態窗體管理」]一章着重講解)。
this
以上說了這麼多了,咱們對於「層級管理」的核心代碼實現,基本都體如今「UI管理器腳本」 (UIManager.cs )中。如下給出具體實現代碼:spa
/***設計
* Title: "SUIFW" 框架技術3d
* 主題: 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)_模態窗體管理" 繼續。