Unity基於UGUI的UI框架

首先

想想我爲什麼要用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()
        {

        }

}//類

}//命名空間

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章