1. 程式人生 > >UI框架—基於UGUI(學習筆記)

UI框架—基於UGUI(學習筆記)

UI框架—基於UGUI

一、需求分析

(一)、需求分析UML圖

1、通過UIManger來自作解析json檔案。

2、UML各種關係

在這裡插入圖片描述

二、知識點

(一)、原理知識

1、單例模式

  • 定義一個靜態的物件 在外界訪問 在內部構造。

  • 構造方法私有化。

     public static UIManager _instance;
    
        public static UIManager Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new UIManager();
                }
                return _instance;
            }
        }
    

(二)、外掛知識

1、使用DOTween外掛來製作panel出現和現實的動畫

  • 匯入外掛,注意名稱空間的引入
  • 其他的根據需求來製作

(三)、操作知識

1、場景的搭建

  • 注意文字格式要設定為跟隨螢幕自適應而改變其大小
  • 在這裡插入圖片描述

(四)、程式碼相關

1、編寫json資訊

  • 編寫json相關資訊

  • 因為使用了unity自帶的兩個json類來解析json所以要把一個整體包裝成另一個整體來讀取

  • json資訊如下

    {
    "infoList":
    [
    {"panelTypeString":"ItemMessage"
    , "path":"UIPanel/ItemMessagePanel"}, {"panelTypeString":"Knapsack", "path":"UIPanel/KnapsackPanel"}, {"panelTypeString":"MainMenu", "path":"UIPanel/MainMenuPanel"}, {"panelTypeString":"Shop", "path":"UIPanel/ShopPanel"}, {"panelTypeString":"Skill", "path":"UIPanel/SkillPanel"}, {"panelTypeString":"System"
    , "path":"UIPanel/SystemPanel"}, {"panelType":"Task", "path":"UIPanel/TaskPanel"} ] }

2、解析json資訊

  • 使用unity自帶的json解析類來解析

  • 使用兩個字典來儲存Panel跟路徑

     private Dictionary<UIPanelType, string> panelPathDict;//儲存所有面板的Prefab的路徑
        private Dictionary<UIPanelType, BasePanel> panelDict;//儲存所有例項化面板的遊戲物體BasePanel元件
        private Stack<BasePanel> panelStack;
    
        [Serializable]
        class UIPanelTypeJson
        {
            public List<UIPanelInfo> infoList;
        }
        /// <summary>
        /// 解析Json資訊
        /// </summary>
        private void ParseUIPanelTypeJson()
        {
            panelPathDict = new Dictionary<UIPanelType, string>();
    
            TextAsset ta = Resources.Load<TextAsset>("UIPanelType");
            UIPanelTypeJson jsonObject = JsonUtility.FromJson<UIPanelTypeJson>(ta.text);
            foreach (UIPanelInfo info in jsonObject.infoList)
            {
                panelPathDict.Add(info.panelType,info.path);//儲存讀取後的名字跟路徑
            }
        }
    
    • UIPanelInfo來讀取裡面的資訊
    using System;
    using System.Collections;
    using UnityEngine;
    
    [Serializable]
    public class UIPanelInfo : ISerializationCallbackReceiver
    {
        [NonSerialized] //不進行讀取
        public UIPanelType panelType;
        public string panelTypeString;  //用來儲存讀取json裡面的地址字串
    
        public string path;
        /// <summary>
        /// 實現介面,讀取json裡面的資訊
        /// 反序列化  從文字資訊到物件
        /// </summary>
        public void OnAfterDeserialize()
        {
            UIPanelType type = (UIPanelType) System.Enum.Parse(typeof(UIPanelType), panelTypeString);
            panelType = type;
        }
        public void OnBeforeSerialize()
        {
            
        }
    }
    

3、擴充套件Dictionary類裡面的方法

  • 原理:有些類是系統定義好的不能修改其中的東西,只能對其中一些東西進行擴充套件,向現有型別“新增”方法,而不建立派生類,

  • 擴充套件類和擴充套件方法都必須是static的,如果擴充套件方法該型別中定義的方法具有相同的名字,則擴充套件方法不會被呼叫

  • 如果擴充套件的父型別更改了,擴充套件方法也失效

    using System.Collections.Generic;
    /// <summary>
    /// 對Dictionary的擴充套件
    /// 必須是靜態方法和靜態類
    /// </summary>
    public static class DictionaryExtension
    {
        /// <summary>
        /// 嘗試根據key得到value,得到了的話直接返回value,沒有得到直接返回null
        /// this Dictionary<Tkey,Tvalue>dict 這個字典表示我們要獲取值的字典
        /// </summary>
        /// <typeparam name="Tkey">泛型一</typeparam>
        /// <typeparam name="Tvalue">泛型二</typeparam>
        /// <param name="dict">方法物件</param>
        /// <param name="key">獲取的值</param>
        /// <returns>value的值</returns>
        public static Tvalue  TryGet<Tkey,Tvalue>(this Dictionary<Tkey,Tvalue>dict,Tkey key)
        {
            Tvalue value;
            dict.TryGetValue(key, out value);
            return value;
        }
    }
    

4、通過CanvasGroup來控制panel的顯示和互動

  • 裡面有一個alpha值來控制顯示和隱藏

  • BlockRaycasts 來控制能不能進行互動

    private CanvasGroup canvasGroup;
    
        void Start()
        {
            if (canvasGroup == null) canvasGroup = GetComponent<CanvasGroup>();
    
        }
    
        public override void OnEnter()
        {
            if (canvasGroup == null) canvasGroup = GetComponent<CanvasGroup>();
            canvasGroup.alpha = 1;
            canvasGroup.blocksRaycasts = true;
    
            Vector3 temp = transform.localPosition;
            temp.x = 600;
            transform.localPosition = temp;
            transform.DOLocalMoveX(0, 0.5f);
        }
    
        public override void OnExit()
        {
            canvasGroup.blocksRaycasts = false;
            transform.DOLocalMoveX(600, 0.5f).OnComplete(() => canvasGroup.alpha = 0);
        }
    
        public override void OnPause()
        {
            canvasGroup.blocksRaycasts = false;
        }
    
        public override void OnResume()
        {
            canvasGroup.blocksRaycasts = true;
        }
    
        public void OnClosePanel()
        {
            UIManager.Instance.PopPanel();
        }
    
        public void OnItemButtonClick()
        {
            UIManager.Instance.PushPanel(UIPanelType.ItemMessage);
        }
    

5、根據面板型別來例項化面板

  • 例項化面板所需要的引數自行設定

    // <summary>
        /// 根據面板型別,得到例項化的面板
        /// </summary>
        /// <param name = "panelType" > 面板的名字 </ param >
        /// < returns ></ returns >
        public BasePanel GetPanel(UIPanelType panelType)
        {
            if (panelDict == null)
            {
                panelDict = new Dictionary<UIPanelType, BasePanel>();
            }
    
            BasePanel panel = panelDict.TryGet(panelType);
            if (panel == null)
            {
               // 乳溝找不到,那麼就找這個面板的prefab的路徑,然後去根據prefab去例項化面板
                string path = panelPathDict.TryGet(panelType);
                GameObject instPanel = GameObject.Instantiate(Resources.Load(path)) as GameObject;
                instPanel.transform.SetParent(CanvasTransform, false);
                panelDict.Add(panelType, instPanel.GetComponent<BasePanel>());
                return instPanel.GetComponent<BasePanel>();
            }
            else
            {
                return panel;
            }
        }
    

6、通過棧來控制面板的進入和移出

  • 把每個panel看做是一個整體進行棧操作。需要的時候進棧,不需要的時候出棧

  • 入棧和入棧

    private Stack<BasePanel> panelStack;  //棧用來存放panel元件
    /// <summary>
        /// 把某個頁面入棧,把某個頁面顯示在介面上
        /// </summary>
        /// <param name = "panelType" > 頁面的名字 </ param >
        public void PushPanel(UIPanelType panelType)
        {
            if (panelStack == null)
                panelStack = new Stack<BasePanel>();
            //判斷一下棧裡面是否有頁面
            if (panelStack.Count > 0)
            {
                BasePanel topPanel = panelStack.Peek();
                topPanel.OnPause();
            }
            BasePanel panel = GetPanel(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) return;
            BasePanel topPanel2 = panelStack.Peek();
            topPanel2.OnResume();
        }
    

7、構建BasePanel 基類來進行面板操作

  • 讓其他Panel來繼承BasePanel

  • 裡面有四個方法,每個方法之間的關係不一樣,詳細檢視UML圖

三、遇到的問題

(一)、老師錯誤

1、Json解析出現報錯

  • Json檔案重複了

  • Json檔案沒有按照Unity裡面的Json解析需求來編輯

(二)、自己的錯誤

1、執行的時候發現報錯,提示Dictionary重複。

  • 描述:Dictionary重複
  • 原因:Json檔案不對,有重複的內容
  • 解決:驗證Json檔案的正確性,在重新讀取
  • 注意:以後如果還是類似問題要優先考慮是檔案讀取的問題。

2、關閉面板不能關閉,也就是不能出棧

  • 描述:關閉面板不能關閉,也就是不能出棧

  • 原因:有一句判斷寫錯了

    if (panelStack.Count > 0) return;//錯誤程式碼
    if (panelStack.Count <= 0) return; //正確程式碼
    
  • 解決:根據出現問題檢查程式碼,步驟如下

    • 先檢查按鈕點選事件看看是否正確。

    • 在檢視關閉按鈕呼叫的是什麼方法

  • 注意:寫程式碼的時候要小心一點,特別是相關if判斷的時候

3、點選商店報錯

  • 描述:要例項化的物件是null。
  • 原因:不知道
  • 解決:從小製作商店Panel
  • 注意:有時候找不到問題就關閉軟體,或者解除元件然後再裝上試試,或者重新制作遊戲物體看看。

四、最後總結

1、UI框架可以為大型一點的遊戲開發出UI所需的框架,而且框架內容寫完以後,每個panel之間的製作就根據具體的開發需求來製作。可以快速的分工製作

2、通過解析Json檔案來讀取需求,能根據需求的變化來進行開發的擴充套件。

3、主要學習了Json的編寫和解析、通過棧來管理Panel、使用CanvasGroup來管理Panel的顯示和互動、還學習了擴充套件方法來拓展Dictionary類、也瞭解了單例模式、以及DOTeen外掛來製作動畫。