您好,登錄后才能下訂單哦!
游戲UI框架設(shè)計(三)
---窗體的層級管理
UI框架中UI窗體的“層級管理”,最核心的問題是如何進行窗體的顯示管理。窗體(預(yù)設(shè))的顯示我們前面定義了三種類型: 普通、隱藏其他、反向切換。代碼如下:
“普通顯示”模式允許多個窗體同時顯示,這種類型應(yīng)用最多。例如RPG中的主城界面(見下圖)。
“隱藏其他界面” 模式一般應(yīng)用于全局性的窗體。我們在開發(fā)此類窗體時,為了減少UI渲染壓力、提高Unity渲染效率,則設(shè)置被覆蓋的窗體為“不可見”狀態(tài)。(即: this.gameObject.SetActive(false))。例如一般的登錄窗體、選擇英雄窗體等。
“反向切換”模式類型,一般都大量引用于“彈出窗體”中。此類窗體的特點是:顯示彈出窗體時不完全覆蓋底層窗體,一般在屏幕的四周會露出底層窗體。之所以命名“反向切換”是因為: 程序員要維護一種“后進先出”的“?!钡臄?shù)據(jù)結(jié)構(gòu)特點,即我們一般要求玩家必須先關(guān)閉彈出的頂層窗體,再依次關(guān)閉下一級窗體。如下圖所示。
上圖即一種典型的彈出窗體。一般我們都要求玩家先處理彈出窗體中的信息,然后關(guān)閉此窗體。一般不允許在沒有關(guān)閉子窗體的情況下,直接點擊父窗體。(關(guān)于彈出窗體時,不允許玩家點擊父窗體的功能實現(xiàn),筆者在下節(jié)[“模態(tài)窗體管理”]一章著重講解)。
以上說了這么多了,我們對于“層級管理”的核心代碼實現(xiàn),基本都體現(xiàn)在“UI管理器腳本” (UIManager.cs )中。以下給出具體實現(xiàn)代碼:
/***
* Title: "SUIFW" 框架技術(shù)
* 主題: UI管理器
* Description:
* 功能:整個UI框架的核心,用戶程序通過調(diào)用本類,來調(diào)用本框架的大多數(shù)功能。
* 功能1:關(guān)于入“棧”與出“?!钡腢I窗體4個狀態(tài)的定義邏輯
* 入棧狀態(tài):
* Freeze(); (上一個UI窗體)凍結(jié)
* Display(); (本UI窗體)顯示
* 出棧狀態(tài):
* Hiding(); (本UI窗體) 隱藏
* Redisplay(); (上一個UI窗體) 重新顯示
* 功能2:增加“非?!本彺婕稀?
*/
using UnityEngine;
usingUnityEngine.UI;
using System;
usingSystem.Collections.Generic;
namespace SUIFW
{
publicclassUIManager : MonoBehaviour
{
/* 字段 */
//本類實例
privatestaticUIManager_Instance = null;
//存儲所有“UI窗體預(yù)設(shè)(Prefab)”路徑
//參數(shù)含義: 第1個string 表示“窗體預(yù)設(shè)”名稱,后一個string 表示對應(yīng)的路徑
privateDictionary<string,string> _DicUIFormsPaths;
//緩存所有已經(jīng)打開的“UI窗體預(yù)設(shè)(Prefab)”
//參數(shù)含義: 第1個string 表示“窗體預(yù)設(shè)”名稱,后一個BaseUI 表示對應(yīng)的“窗體預(yù)設(shè)”
privateDictionary<string,BaseUIForms> _DicALLUIForms;
//“?!苯Y(jié)構(gòu)表示的“當(dāng)前UI窗體”集合。
privateStack<BaseUIForms>_StaCurrentUIForms;
//當(dāng)前顯示狀態(tài)的UI窗體集合
privateDictionary<string,BaseUIForms> _DicCurrentShowUIForms;
//UI根節(jié)點
privateTransform _CanvasTransform = null;
//普通全屏界面節(jié)點
privateTransform _CanTransformNormal = null;
//固定界面節(jié)點
privateTransform _CanTransformFixed = null;
//彈出模式節(jié)點
privateTransform _CanTransformPopUp = null;
//UI腳本節(jié)點(加載各種管理腳本的節(jié)點)
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根節(jié)點、及其重要子節(jié)點
_CanvasTransform = GameObject.FindGameObjectWithTag(SysDefine.SYS_TAG_CANVAS).transform;
//得到普通全屏界面節(jié)點、固定界面節(jié)點、彈出模式節(jié)點、UI腳本節(jié)點
_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的子節(jié)點
UnityHelper.AddChildToParent(_CanTransformUIScripts,this.gameObject.transform);
//本UI節(jié)點信息,場景轉(zhuǎn)換時,不允許銷毀
DontDestroyOnLoad(_CanvasTransform);
//初始化“UI窗體預(yù)設(shè)”路徑數(shù)據(jù)
InitUIFormsPathsData();
}
///<summary>
///顯示UI窗體
///</summary>
///<param name="strUIFormName">UI窗體的名稱</param>
publicvoid ShowUIForms(stringstrUIFormName)
{
BaseUIFormsbaseUIForms; //UI窗體基類
//參數(shù)檢查
if(string.IsNullOrEmpty(strUIFormName)) return;
//加載“UI窗體名稱”,到“所有UI窗體緩存”中
baseUIForms =LoadUIFormsToAllUIFormsCatch(strUIFormName);
if(baseUIForms == null) return;
//判斷是否清空“?!苯Y(jié)構(gòu)體集合
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>
///關(guān)閉或返回上一個UI窗體(關(guān)閉當(dāng)前UI窗體)
///</summary>
publicvoid CloseOrReturnUIForms(stringstrUIFormName)
{
BaseUIFormsbaseUIForms = null; //UI窗體基類
/* 參數(shù)檢查 */
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>
///根據(jù)指定UI窗體名稱,加載到“所有UI窗體”緩存中。
///</summary>
///<param name="strUIFormName">UI窗體名稱</param>
///<returns></returns>
privateBaseUIForms LoadUIFormsToAllUIFormsCatch(stringstrUIFormName)
{
BaseUIFormsbaseUI; //UI窗體
//判斷“UI預(yù)設(shè)緩存集合”是否有指定的UI窗體,否則新加載窗體
_DicALLUIForms.TryGetValue(strUIFormName, outbaseUI);
if(baseUI == null)
{
//加載指定路徑的“UI窗體”
baseUI =LoadUIForms(strUIFormName);
}
returnbaseUI;
}
///<summary>
///加載UI窗體到“當(dāng)前顯示窗體集合”緩存中。
///</summary>
///<param name="strUIFormsName"></param>
privatevoid EnterUIFormsCache(stringstrUIFormsName)
{
BaseUIFormsbaseUIForms; //UI窗體基類
BaseUIFormsbaseUIFormsFromAllCache; //"所有窗體集合"中的窗體基類
//“正在顯示UI窗體緩存”集合里有記錄,則直接返回。
_DicCurrentShowUIForms.TryGetValue(strUIFormsName, outbaseUIForms);
if(baseUIForms != null) return;
//把當(dāng)前窗體,加載到“正在顯示UI窗體緩存”集合里
_DicALLUIForms.TryGetValue(strUIFormsName, outbaseUIFormsFromAllCache);
if(baseUIFormsFromAllCache != null)
{
_DicCurrentShowUIForms.Add(strUIFormsName, baseUIFormsFromAllCache);
baseUIFormsFromAllCache.Display();
}
}
///<summary>
///卸載UI窗體從“當(dāng)前顯示窗體集合”緩存中。
///</summary>
///<paramname="strUIFormsName"></param>
privatevoid ExitUIFormsCache(stringstrUIFormsName)
{
BaseUIFormsbaseUIForms; //UI窗體基類
//“正在顯示UI窗體緩存”集合沒有記錄,則直接返回。
_DicCurrentShowUIForms.TryGetValue(strUIFormsName, outbaseUIForms);
if(baseUIForms == null) return;
//指定UI窗體,運行隱藏狀態(tài),且從“正在顯示UI窗體緩存”集合中移除。
baseUIForms.Hiding();
_DicCurrentShowUIForms.Remove(strUIFormsName);
}
///<summary>
///加載UI窗體到“當(dāng)前顯示窗體集合”緩存中,且隱藏其他正在顯示的頁面
///</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();
}
//把當(dāng)前窗體,加載到“正在顯示UI窗體緩存”集合里
_DicALLUIForms.TryGetValue(strUIFormsName,out baseUIFormsFromAllCache);
if(baseUIFormsFromAllCache != null)
{
_DicCurrentShowUIForms.Add(strUIFormsName, baseUIFormsFromAllCache);
baseUIFormsFromAllCache.Display();
}
}
///<summary>
///卸載UI窗體從“當(dāng)前顯示窗體集合”緩存中,且顯示其他原本需要顯示的頁面
///</summary>
///<paramname="strUIFormsName"></param>
privatevoidExitUIFormsFromCacheAndShowOther(stringstrUIFormsName)
{
BaseUIFormsbaseUIForms; //UI窗體基類
//“正在顯示UI窗體緩存”集合沒有記錄,則直接返回。
_DicCurrentShowUIForms.TryGetValue(strUIFormsName, outbaseUIForms);
if(baseUIForms == null) return;
//指定UI窗體,運行隱藏狀態(tài),且從“正在顯示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: 判斷棧里是否已經(jīng)有窗體,有則“凍結(jié)”
/// 2: 先判斷“UI預(yù)設(shè)緩存集合”是否有指定的UI窗體,有則處理。
/// 3: 指定UI窗體入"棧"
///</summary>
///<paramname="strUIFormsName"></param>
privatevoid PushUIForms(stringstrUIFormsName)
{
BaseUIFormsbaseUI; //UI預(yù)設(shè)窗體
//判斷棧里是否已經(jīng)有窗體,有則“凍結(jié)”
if(_StaCurrentUIForms.Count > 0)
{
BaseUIFormstopUIForms = _StaCurrentUIForms.Peek();
topUIForms.Freeze();
}
//先判斷“UI預(yù)設(shè)緩存集合”是否有指定的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:根據(jù)“UI窗體預(yù)設(shè)”名稱,加載預(yù)設(shè)克隆體。
/// 2:預(yù)設(shè)克隆體添加UI“根節(jié)點”為父節(jié)點。
/// 3:隱藏剛創(chuàng)建的UI克隆體。
/// 4:新創(chuàng)建的“UI窗體”,加入“UI窗體緩存”中
///</summary>
privateBaseUIForms LoadUIForms(stringstrUIFormsName)
{
stringstrUIFormsPaths = null; //UI窗體的路徑
GameObjectgoCloneUIPrefab = null; //克隆的"窗體預(yù)設(shè)"
BaseUIFormsbaseUIForm; //UI窗體
//得到UI窗體的路徑
_DicUIFormsPaths.TryGetValue(strUIFormsName, outstrUIFormsPaths);
//加載指定路徑的“UI窗體”
if(!string.IsNullOrEmpty(strUIFormsPaths))
{
goCloneUIPrefab = ResourcesMgr.GetInstance().LoadAsset(strUIFormsPaths,false);
}
//設(shè)置“UI窗體”克隆體的父節(jié)點,以及隱藏處理與加入“UI窗體緩存”中
if(_CanvasTransform != null&& goCloneUIPrefab != null)
{
baseUIForm = goCloneUIPrefab.GetComponent<BaseUIForms>();
if(baseUIForm == null)
{
Log.Write(GetType()+ string.Format("/LoadUIForms()/ baseUIForm==null!,請先確認(rèn)克隆對象上是否加載了BaseUIForms的子類。參數(shù) 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);
//新創(chuàng)建的“UI窗體”,加入“UI窗體緩存”中
_DicALLUIForms.Add(strUIFormsName, baseUIForm);
returnbaseUIForm;
}
else
{
Log.Write(GetType()+ string.Format("/LoadUIForms()/‘_CanvasTransform’ Or ‘goCloneUIPrefab’==NULL! , 方法參數(shù)strUIFormsName={0},請檢查!", strUIFormsName), Log.Level.High);
}
Log.Write(GetType()+ string.Format("/LoadUIForms()/ 出現(xiàn)不可預(yù)知錯誤,請檢查! 方法參數(shù)strUIFormsName={0}", strUIFormsName), Log.Level.High);
returnnull;
}
///<summary>
///初始化項目開始必須的資源加載
///</summary>
privatevoid InitRootCanvasLoading()
{
if(UnityHelper.isFirstLoad)
{
ResourcesMgr.GetInstance().LoadAsset(SysDefine.SYS_PATH_CANVAS, false);
}
}
///<summary>
///初始化“UI窗體預(yù)設(shè)”路徑數(shù)據(jù)
///</summary>
privatevoid InitUIFormsPathsData()
{
//測試也成功
IConfigManagerconfigMgr = newConfigManagerByJson(SysDefine.SYS_PATH_UIFormConfigJson);
if(_DicUIFormsPaths != null)
{
_DicUIFormsPaths =configMgr.AppSetting;
}
}
///<summary>
///清空“棧”結(jié)構(gòu)體集合
///</summary>
///<returns></returns>
privatebool ClearStackArray()
{
if(_StaCurrentUIForms != null&& _StaCurrentUIForms.Count >= 1)
{
_StaCurrentUIForms.Clear();
returntrue;
}
returnfalse;
}
#endregion
}//Class_end
}
以上代碼解釋:
1: UIManager.cs 中定義的新的字段 ,“_StaCurrentUIForms” 就是一個“棧”數(shù)據(jù)類型,用于維護一種后進先出的數(shù)據(jù)結(jié)構(gòu)。常見的方法如下:
C#語言中提供 Stack<T> 泛型集合,來直接實現(xiàn)這種結(jié)構(gòu)。
常用屬性與方法:
Count 屬性 查詢棧內(nèi)元素數(shù)量
Push() 壓棧
Pop() 出棧
Peek() 查詢棧頂元素
GetEnumerator() 遍歷棧中所有元素
2: UIManager.cs 中的“ShowUIForms()”方法中的PushUIForms()與EnterUIFormstToCacheHideOther() 方法,就是專門處理“反向切換”與“隱藏其他”窗體特性的實現(xiàn)方法。
好了時間不早了就先寫到這吧,大家有什么疑問可以討論,這里筆者也主要是想起到“拋磚引玉”的作用。
本篇就先寫到這,下篇 "游戲UI框架設(shè)計(4)_模態(tài)窗體管理" 繼續(xù)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。