基于Unity~UGUI的简单UI框架

UI框架总揽:

(如果开发过程测试中遇到找不到面板 报空的错误异常 请仔细检查json文件 查看是否名称和路径存在拼写错误的情况 如果在VS中看不出异常 最好在文件夹中打开json文件检查一下 因为有些字符在VS中并不明显 如空格 )

在这里插入图片描述

创建测试面板(非框架部分)

将面板以Prefab形式放入Resources文件夹下面,便于框架加载面板
(注意Resources不要拼写错误,否则会读取出错)
图一

创建json文件和UIPanelType枚举类来保存所有的面板信息

UIPanelType.cs:

每添加一个面板都要在此枚举类里面添加对应面板的类型

public enum UIPanelType 
 {
	ItemMessage,
    Knapsack,
    MainMenu,
    Shop,
    Skill,
    System,
    Task
}
UIPanelType.json(保存面板信息的json文件):

1.此文件需要加载,保存到Resources文件下
2.保存了面板的类型(相当于名称)和面板Prefab的路径(实例化面板时要用到)
3.每添加一个面板,都要在此文件添加该面板对应的信息

此文本信息尤为重要 在反序列化时(从文本信息到对象) 需要通过此文本信息的名称和路径找到图一Resources文件夹下我们之前创建好的面板预置体 将其正确实例化出来

{
"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"},

	{"panelTypeString":"Task",
	"path":"UIPanel/TaskPanel"}
]
}
UIPanelInfo(面板信息集合类)

用于和json文件进行映射()

[Serializable]
public class UIPanelInfo:ISerializationCallbackReceiver {
    [NonSerialized]
    public UIPanelType panelType;
    public string panelTypeString;
    //{
    //    get
    //    {
    //        return panelType.ToString();
    //    }
    //    set
    //    {
    //        UIPanelType type = (UIPanelType)System.Enum.Parse(typeof(UIPanelType), value);
    //        panelType = type;
    //    }
    //}
    public string path;

    //反序列化   从文本信息 到对象
    public void OnAfterDeserialize()
    {
        //Debug.Log(panelTypeString);      
        UIPanelType type = (UIPanelType)System.Enum.Parse(typeof(UIPanelType),panelTypeString);
        //将字符串型转化为(Enum)枚举类型。   
        //  例如:现在有个字符串sString,一个枚举EnumName,希望把sString类型转换成EnumName类型格式如下:
        //(EnumName)EnumName.Parse(typeof(EnumName), sString)
        panelType = type;
    }
    public void OnBeforeSerialize()
    {
       
    }
}

开发UIManger来解析面板信息json文件(框架核心:UIManager)

UIManager为框架的核心,全局只有一个UIManager,因此UIManager要使用单例模式

现阶段的完整的UIManager.cs(后续还会继续完善):
public class UIManager {

    /// <summary>
    /// 单例模式的核心
    /// 1.定义一个静态的对象  在外界访问 在内部构造
    /// 2.构造方法私有化
    /// </summary>
    private static UIManager _instance;
    public static UIManager Instance
    {
        get
        {
            if (_instance==null)
            {
                _instance = new UIManager();
            }
            return _instance;
        }
    }

private Dictionary<UIPanelType, string> panelPathDict;//存储所有面板Prefab的路径

private UIManager()
    {
        ParseUIPanelTypeJson();
    }

 [Serializable]
    class UIPanelTypeJson
    {
        public List<UIPanelInfo> infoList;
    }
    
    private void ParseUIPanelTypeJson()
    {
        panelPathDict = new Dictionary<UIPanelType, string>();
        TextAsset ta = Resources.Load<TextAsset>("UIPanelType");//读取Resources文件夹下的
       // “UIPanelType”json文件
       UIPanelTypeJson jsonObject = JsonUtility.FromJson<UIPanelTypeJson>(ta.text);
         //将json文件转化成对象                                                                                 
         //这将创建一个新的实例MyClass并使用JSON数据在其上设置值。
         //如果JSON数据包含不映射到MyClass,
        //然后,这些值将被忽略,如果JSON数据缺少MyClass,则这些字段将留在其构造的值中返回的对象中。

        foreach (UIPanelInfo info in jsonObject.infoList)
        {
            panelPathDict.Add(info.panelType, info.path);//将转化出来的对象信息存进字典里去
        }
    }

开发BasePanel抽象类(各个UIPanel的基类)

BasePanel给每个Panel定义了四个事件:
OnEnter:面板进入时调用
OnPause:面板停止时调用(鼠标与面板的交互停止)
OnResume:面板恢复使用时调用(鼠标与面板的交互恢复)
OnExit:面板退出时调用

using UnityEngine;

public class BasePanel : MonoBehaviour
{
    public abstract void OnEnter(){      }

    public abstract void OnPause(){      }

    public abstract void OnResume(){      }

    public abstract void OnExit(){      }

}
给各个面板添加UIPanel类(非框架部分)

(此处就不把全部面板的脚本都放上来了 )
在这里插入图片描述

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;

public class KnapsackPanel :BasePanel {

    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, .5f);
    }

    public override void OnExit()
    {
        //canvasGroup.alpha = 0;
        canvasGroup.blocksRaycasts = false;
        //设置面板的离场动画
        transform.DOLocalMoveX(600, .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);
    }
}

对UI面板Prefab实例化的创建与管理

获取Panel(如果panel没有被实例化,进行实例化,并且存储到已经实例化好的panel字典中) (这部分代码也是在UIManager中实现的)
private Dictionary<UIPanelType, BasePanel> panelDict; //保存所有实例化面板的游戏物体身上的BasePanel组件

 /// <summary>
    /// 根据面板类型   得到实例化的面板
    /// </summary>
    /// <param name="panelType"></param>
    /// <returns></returns>
    private BasePanel GetPanel(UIPanelType panelType)
    {
    //如果字典不存在  则构造一个新的字典
        if (panelDict ==null)
        {
            panelDict = new Dictionary<UIPanelType, BasePanel>();
        }
        BasePanel panel;
        panelDict.TryGetValue(panelType, out panel);  

        //BasePanel panel = panelDict.TryGet(panelType);  //方法①

        if (panel==null)
        {
            //如果找不到,那么就找这个面板prefab的路径,然后去根据prefab去实例化面板
            //string path;     //方法①
            //panelPathDict.TryGetValue(panelType, out path);//方法①
            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;
        }
    }
字典的扩展:
/// <summary>
/// 对Dictionary的扩展
/// </summary>
public static class DictionaryExtension {

    public static Tvalue TryGet<Tkey, Tvalue>(this Dictionary<Tkey, Tvalue> dict, Tkey key)
    {
        Tvalue value;
        dict.TryGetValue(key, out value);
        return value;
    }	
}

开发界面存储栈,并进行出栈和入栈操作

【设计理念】 (这部分代码也是在UIManager中实现的)

1.鼠标只允许和一个界面进行交互,当一个界面显示后,上一个界面要停止和鼠标的交互
2.界面显示,进行入栈操作(Push,OnEnter),停止上一个界面(OnPause)
3.界面隐藏,进行出栈操作(Pop,OnExit),恢复上一个界面(OnResume)

/// <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 = 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();
    }

控制Panel之间的跳转(非框架部分)

【GameRoot】 实例化游戏中的主面板
public class GameRoot : MonoBehaviour 
{
    private void Start()
    {
         UIManager.Instance.PushPanel(UIPanelType.MainMenu);
       //UIManager.Instance.Test();
    }
}

完善面板中的一些操作,实现面板之间的跳转,此部分不是框架部分,省略!

using UnityEngine;
using UnityEngine.UI;

public class MainMenuPanel : BasePanel
{
    private Button TaskButton;
    private Button KnapsackButton;
    private Button SkillButton;
    private Button ShopButton;
    private Button SystemButton;
    private CanvasGroup canvasGroup;
    void Awake()
    {
        TaskButton = transform.Find("IconPanel/TaskButton").GetComponent<Button>();
        KnapsackButton = transform.Find("IconPanel/KnapsackButton").GetComponent<Button>();
        SkillButton = transform.Find("IconPanel/SkillButton").GetComponent<Button>();
        ShopButton = transform.Find("IconPanel/ShopButton").GetComponent<Button>();
        SystemButton = transform.Find("IconPanel/SystemButton").GetComponent<Button>();
        canvasGroup = transform.GetComponent<CanvasGroup>();

        TaskButton.onClick.AddListener(OnTaskButtonClick);
        KnapsackButton.onClick.AddListener(OnKnapsackButtonClick);
        SkillButton.onClick.AddListener(OnSkillButtonClick);
        ShopButton.onClick.AddListener(OnShopButtonClick);
        SystemButton.onClick.AddListener(OnSystemButtonClick);
    }

    void OnDestory()
    {
        TaskButton.onClick.RemoveListener(OnTaskButtonClick);
        KnapsackButton.onClick.RemoveListener(OnKnapsackButtonClick);
        SkillButton.onClick.RemoveListener(OnSkillButtonClick);
        ShopButton.onClick.RemoveListener(OnShopButtonClick);
        SystemButton.onClick.RemoveListener(OnSystemButtonClick);
    }
    public override void OnEnter()
    {
    }

    public override void OnPause()
    {
        canvasGroup.blocksRaycasts = false;
    }

    public override void OnResume()
    {
        canvasGroup.blocksRaycasts = true;
    }

    public override void OnExit()
    {
    }

    private void OnTaskButtonClick()
    {
        UIPanelManager.Instance.PushPanel(UIPanelType.Task);
    }
    private void OnKnapsackButtonClick()
    {
        UIPanelManager.Instance.PushPanel(UIPanelType.Knapsack);
    }
    private void OnSkillButtonClick()
    {
        UIPanelManager.Instance.PushPanel(UIPanelType.Skill);
    }
    private void OnShopButtonClick()
    {
        UIPanelManager.Instance.PushPanel(UIPanelType.Shop);
    }
    private void OnSystemButtonClick()
    {
        UIPanelManager.Instance.PushPanel(UIPanelType.System);
    }
}

在这里插入图片描述

最后附上UIManager的完整代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public class UIManager {


    /// <summary>
    /// 单例模式的核心
    /// 1.定义一个静态的对象  在外界访问 在内部构造
    /// 2.构造方法私有化
    /// </summary>
    private static UIManager _instance;

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

    private Transform canvasTransform;
    private Transform CanvasTransform
    {
        get
        {
            if (canvasTransform == null)
            {
                canvasTransform = GameObject.Find("Canvas").transform;
            }
            return canvasTransform;
        }
    }
    private Dictionary<UIPanelType, string> panelPathDict;//存储所有面板Prefab的路径
    private Dictionary<UIPanelType, BasePanel> panelDict; //保存所有实例化面板的游戏物体身上的BasePanel组件
    private Stack<BasePanel> panelStack;

    private UIManager()
    {
        ParseUIPanelTypeJson();
    }

    /// <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 = 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();
    }


    /// <summary>
    /// 根据面板类型   得到实例化的面板
    /// </summary>
    /// <param name="panelType"></param>
    /// <returns></returns>
    private BasePanel GetPanel(UIPanelType panelType)
    {
        if (panelDict ==null)
        {
            panelDict = new Dictionary<UIPanelType, BasePanel>();
        }
        BasePanel panel;
        panelDict.TryGetValue(panelType, out panel);  //TODO

        //BasePanel panel = panelDict.TryGet(panelType);

        if (panel==null)
        {
            //如果找不到,那么就找这个面板prefab的路径,然后去根据prefab去实例化面板
            //string path;
            //panelPathDict.TryGetValue(panelType, out path);
            string path = panelPathDict.TryGet(panelType);
            //Debug.Log(path);
            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;
        }
    }

    [Serializable]
    class UIPanelTypeJson
    {
        public List<UIPanelInfo> infoList;
    }
    
    private void ParseUIPanelTypeJson()
    {
        panelPathDict = new Dictionary<UIPanelType, string>();

        TextAsset ta = Resources.Load<TextAsset>("UIPanelType");//读取Resources文件夹下的“UIPanelType”json文件

       UIPanelTypeJson jsonObject = JsonUtility.FromJson<UIPanelTypeJson>(ta.text);//将json文件转化成对象                                                                                   //这将创建一个新的实例MyClass并使用JSON数据在其上设置值。如果JSON数据包含不映射到MyClass,
        //然后,这些值将被忽略,如果JSON数据缺少MyClass,则这些字段将留在其构造的值中返回的对象中。

        foreach (UIPanelInfo info in jsonObject.infoList)
        {
            panelPathDict.Add(info.panelType, info.path);//将转化出来的对象信息存进字典里去
        }
    }

    public void Test()
    {
        string path;
        panelPathDict.TryGetValue(UIPanelType.Knapsack, out path);
        //  Debug.Log(path);
        //bool isContain = panelPathDict.ContainsValue("UIPanel/ItemMessagePanel");
        bool isContain = panelPathDict.ContainsKey(UIPanelType.MainMenu);
        bool isContain1 = panelPathDict.ContainsKey(UIPanelType.Shop);
        //Debug.Log(isContain);
        //Debug.Log(isContain1);
    }	
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值