首先
想想我爲什麼要用UI框架,因爲不用UI框架會導致遊戲混亂,管理困難?
那麼體現在什麼方面呢?
一般在設計遊戲的時候,會對各個UI面板進行相應的管理,保證同一時間,玩家只對一個UI面板進行操作,保證不會因爲玩家亂點,而導致玩家不知道哪個是哪個,或者哪個在哪,還有就是說,讓玩家需要哪個UI面板,玩家點擊哪個面板的時候再進行相應的實例化加載,不是一股腦的上來全加載出來,等着玩家點按鈕調用,那樣性能上受不了。
再來想,在UGUI下,UIA和UIB堆在一起誰能顯示出來?誰會被覆蓋?這個取決於他們在Hierarchy面板的順序,誰在下面,誰就能被看到,而另一個則會被這一個給覆蓋住,而顯示不出來。我每實例化出來一個UI面板,都會相應的覆蓋住上一個UI面板,然後,當我不用這個UI面板的時候怎麼辦?我能想到的有三種選擇,一個是移動到屏幕顯示的外邊,讓玩家看不到它。二是直接銷燬它。三是使用我們的UI框架的棧思想。
但是,第一個會存在什麼問題,UI面板的顯示完全依賴於Hierarchy面板的順序,也就是說,每次遊戲過程中,這些UI面板誰能遮擋住誰,是根據玩家點擊順序決定的,如果在允許玩家不關上當前窗口的時候進行其他操作的話,玩家誇誇誇的連續點擊其他的UI面板,然後再跳出一堆其他的窗口,玩家再想在這些窗口裏找到它想要的窗口的話,是非常困難的,因爲實例化的順序不一樣,你得按照最上面的那個面板是啥,得一個個對應着關了,才能找到玩家想要的那個面板,再進行操作,當然如果能做好相應的措施還行,不過這麼做在UI面板多的情況下,真的會很麻煩。
第二個則不會出現這種問題,但是第二個帶來的問題是卡頓,玩過王者榮耀的應該知道,你再大廳界面,點擊揹包,如果你手機配置很高,則不會那麼明顯,這裏說一般手機,當你剛剛打開遊戲,點擊揹包按鈕的時候,你的手機極有可能會頓那麼一點幾秒,這個過程就是在實例化揹包的面板,但是當你關閉了揹包之後,再打開,爲什麼就不卡了?因爲揹包面板已經被實例化出來了,你剛纔將他關閉只是隱藏了它而已。但是如果王者榮耀採用第二種方式呢?當你需要頻繁操作某個有很多數據的UI的時候,普通手機的體驗會真的很不好。
第三個就牛逼多了。因爲加入了類Windows的模態窗口的約束,所以不依賴於Hierarchy面板的順序,該怎麼顯示就怎麼顯示,根據所需實例化所需UI面板,然後需要顯示就入棧,需要隱藏就依次出棧,而這些只需要在框架搭好後的幾句代碼,即可實現,而且只在需要的時候加載對應的UI面板,並存起來,需要的時候再讓它顯示出來,不浪費性能。
UI框架的思想:
和棧的思想基本相同,在開發Winform程序和MFC程序的時候,肯定遇到過模態窗口和非模態窗口這個概念,
定義:對話框分爲模態對話框和非模態對話框兩種。二者的區別在於當對話框打開時,是否允許用戶進行其他對象的操作。
草圖構思:
也是就說要實現下面這樣的功能:
當我點擊一個按鈕,出現對應模塊的時候,我必須關閉這個模塊,才能對其他的模塊進行操作。
也就是先進後出,我必須倒序的關閉我打開的一切窗口,才能對其他模塊進行操作。也就是不斷的進行Push操作和Pop操作。
下面來看下代碼實現:
這個是UIManager類 ,整個框架的核心,包含了讀取Json數據,到實例化UI面板,再到隱藏UI面板的一切功能。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using LitJson;
using System;
/*
*作者:琦玉老師的二弟子
*/
namespace JumpAgent
{
public class UIManager
{
private GameObject ShowCanvas;
/// <summary>
/// 單例模式
/// </summary>
private static UIManager _instance;
/// <summary>
/// 存儲所有UI面板的路徑的字典
/// </summary>
private Dictionary<UIPanelType, string> panelPathDict;
/// <summary>
/// 保存所有實例化出來的UI面板遊戲物體身上的BasePanel組件
/// </summary>
private Dictionary<UIPanelType, BasePanel> panelDict;
/// <summary>
/// UI面板棧,所有入棧的都是顯示出來的
/// </summary>
private Stack<BasePanel> panelStack;
/// <summary>
/// 構造方法,讀取數據
/// </summary>
private UIManager()
{
ShowCanvas = GameObject.Find("Canvas");
ParseUIPanelTypeJson();
}
/// <summary>
/// 取得單例
/// </summary>
public static UIManager Instance
{
get
{
if (_instance == null)
{
_instance = new UIManager();
}
return _instance;
}
}
/// <summary>
/// 把Json轉化成數據
/// </summary>
private void ParseUIPanelTypeJson()
{
//初始化UI面板字典
panelPathDict = new Dictionary<UIPanelType, string>();
//加載Json文件
TextAsset ta = Resources.Load<TextAsset>("UIPanelType");
//取得Json數據轉換成的對象,存到集合裏面
List<UIPanleInfo> panleInfoList = JsonMapper.ToObject<List<UIPanleInfo>>(ta.text);
//把集合裏的對象存到UIPanel的字典裏
foreach (UIPanleInfo info in panleInfoList)
{
panelPathDict.Add(info.panelType, info.path);
}
}
/// <summary>
/// 得到一個UI面板,如果該UI面板沒有被實例化過,則創建
/// </summary>
/// <param name="panelType"></param>
public BasePanel GetUIPanel(UIPanelType panelType)
{
if (panelDict==null)
{
panelDict = new Dictionary<UIPanelType, BasePanel>();
}
//BasePanel basePanel;
//panelDict.TryGetValue(panelType, out basePanel);
//擴展方法
BasePanel basePanel = panelDict.TryGet(panelType);
if (basePanel==null)
{
string path;
panelPathDict.TryGetValue(panelType, out path);
GameObject InsPanel=(GameObject)GameObject.Instantiate(Resources.Load(path));
InsPanel.transform.SetParent(ShowCanvas.gameObject.transform,false);
panelDict.Add(panelType, InsPanel.GetComponent<BasePanel>());
return InsPanel.GetComponent<BasePanel>();
}
else
{
return basePanel;
}
}
/// <summary>
/// 入棧,把某個頁面顯示在界面上
/// </summary>
public void PushPanel(UIPanelType panelType)
{
if (panelStack==null)
{
panelStack = new Stack<BasePanel>();
}
if (panelStack.Count>0)
{
BasePanel topPanel = panelStack.Peek();
topPanel.OnPause();
}
BasePanel panel = GetUIPanel(panelType);
panel.OnEnter();
panelStack.Push(panel);
}
/// <summary>
/// 出棧,把某個頁面從界面上移除
/// </summary>
public void PopPanel()
{
if (panelStack == null)
{
panelStack = new Stack<BasePanel>();
}
if (panelStack.Count==0)
{
return;
}
//關閉棧頂頁面的顯示
BasePanel topPanel = panelStack.Pop();
topPanel.OnExit();
if (panelStack.Count != 0)
{
BasePanel nextPanel = panelStack.Peek();
nextPanel.OnResume();
}
}
/// <summary>
/// 測試方法
/// </summary>
public void Test()
{
foreach (var item in panelPathDict)
{
Debug.Log("Panel類型爲:" + item.Key + "__路徑爲:" + item.Value);
}
}
}//類
}//命名空間
再然後是所有BasePanel,即所有UI面板的基類:繼承這個基類的子類UI面板需要重寫裏面的這四個虛方法,以供我們的UIManager調用。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
/*
作者:琦玉老師的二弟子
*/
namespace JumpAgent{
public class BasePanel : MonoBehaviour
{
/// <summary>
/// 顯示界面
/// </summary>
public virtual void OnEnter()
{
}
/// <summary>
/// 界面暫停
/// </summary>
public virtual void OnPause()
{
}
/// <summary>
/// 界面繼續
/// </summary>
public virtual void OnResume()
{
}
/// <summary>
/// 界面推出
/// </summary>
public virtual void OnExit()
{
}
}//類
}//命名空間