Unity-基础UI框架的编写

目录

第一部分:UIFrame_1.0

一、搭建UI界面及要求

二、实现上述功能代码中的缺点:

1、在BagPanel中找Canvas调用RolePanel,耦合度高

2、最开始RolePanel是隐藏状态,使用transform.Find()有概率找不到物体

三、改进:设置一个UIManager,控制管理每一个Panel的状态

四、UIFrame1.0中还是存在的缺点:

1、每个面板的引用都需要单独的写出,项目复杂度高了就不好处理

2、会导致内存的暴涨,统一关闭是效率较差

3、每一个面板需要单独写一个函数,后期的扩展性太差

第二部分:UIFrame_2.0

一、新要求:


前言:需要基础:MVC思想,UGUI,XML基础

小知识点:

1.Resources:在打包的过程中,无论文件夹中资源有没有用,都会打包出来,打包后,这个文件夹会以一种压缩的形式存在,无法在打包后的文件中找到(有上限,最多只能存放4G文件)

2.泛型约束

struct,class,类名、接口名(继承约束),new():必须有一个公共的无参构造函数

 3.UI组件-Grid Layout Group

第一部分:UIFrame_1.0

一、搭建UI界面及要求

1、点击右下角图标可以出现一个面板

 2、面板中有一个叉叉❌,点击❌面板关闭

3、背包面板中需要有一个按钮,点击按钮可以打开人物属性面板,查看人物属性(RolePanel)

 实现代码:MainPanel

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

public class MainPanel : MonoBehaviour
{
    Button btn_setting;
    Button btn_skill;
    Button btn_bag;
    Button btn_role;

    GameObject mainPanel;
    GameObject rolePanel;
    GameObject bagPanel;
    GameObject settingPanel;
    GameObject skillPanel;

    Transform BtnGroup;
    Transform Canvas;


    void Awake()
    {
        BtnGroup = transform.Find("BtnGroup");
        Canvas = transform.parent;
        Debug.Log(Canvas);

        btn_setting = BtnGroup.Find("Btn_Setting").GetComponent<Button>();
        btn_skill = BtnGroup.Find("Btn_Skill").GetComponent<Button>();
        btn_bag = BtnGroup.Find("Btn_Bag").GetComponent<Button>();
        btn_role = BtnGroup.Find("Btn_Role").GetComponent<Button>();

        settingPanel = Canvas.Find("SettingPanel").gameObject;
        skillPanel = Canvas.Find("SkillPanel").gameObject;
        bagPanel = Canvas.Find("BagPanel").gameObject;
        rolePanel = Canvas.Find("RolePanel").gameObject;


    }

    // Start is called before the first frame update
    void Start()
    {

        btn_setting.onClick.AddListener(() => { settingPanel.SetActive(!settingPanel.activeSelf); });
        btn_skill.onClick.AddListener(() => { skillPanel.SetActive(!skillPanel.activeSelf); });
        btn_bag.onClick.AddListener(() => { bagPanel.SetActive(!bagPanel.activeSelf); });
        btn_role.onClick.AddListener(() => { rolePanel.SetActive(!rolePanel.activeSelf); });

    }

    // Update is called once per frame
    void Update()
    {

    }
}

BagPanel:

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

public class BagPanel : MonoBehaviour
{
    Transform RolePanel;
    Button btn_RolePanel;
    Transform Canvas;

    Transform Btn_close;
    Button btn_close;

    private void Awake()
    {
        Canvas = transform.parent;
        RolePanel = Canvas.Find("RolePanel");
        btn_RolePanel = transform.Find("Btn_RolePanel").GetComponent<Button>();

        Btn_close = GameObject.Find("Btn_close").transform;
        btn_close = Btn_close.GetComponent<Button>();   
            
    }
    // Start is called before the first frame update
    void Start()
    {
        btn_close.onClick.AddListener(() => {gameObject.SetActive(false); });
        btn_RolePanel.onClick.AddListener(() => { RolePanel.gameObject.SetActive(true); });
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

二、实现上述功能代码中的缺点:

1、在BagPanel中找Canvas调用RolePanel,耦合度高

2、最开始RolePanel是隐藏状态,使用transform.Find()有概率找不到物体

三、改进:设置一个UIManager,控制管理每一个Panel的状态

UIManager脚本:

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

public class UIManager : MonoBehaviour
{
    //UIManager一般会绑定在GameRoot脚本上面
    GameObject Canvas;
    GameObject mainPanel;
    GameObject rolePanel;
    GameObject bagPanel;
    GameObject settingPanel;
    GameObject skillPanel;

    Button btn_setting;
    Button btn_skill;
    Button btn_bag;
    Button btn_role;

    Transform BtnGroup;

    private static UIManager _instance;
    public static UIManager Instance
    {
        get
        {
            return _instance;
        }
    }

    private void Awake()
    {
        _instance = this;

        Canvas = GameObject.Find("Canvas");
        mainPanel = Canvas.transform.Find("MainPanel").gameObject;
        settingPanel = Canvas.transform.Find("SettingPanel").gameObject;
        skillPanel = Canvas.transform.Find("SkillPanel").gameObject;
        bagPanel = Canvas.transform.Find("BagPanel").gameObject;
        rolePanel = Canvas.transform.Find("RolePanel").gameObject;

        BtnGroup = mainPanel.transform.Find("BtnGroup");
        btn_setting = BtnGroup.Find("Btn_Setting").GetComponent<Button>();
        btn_skill = BtnGroup.Find("Btn_Skill").GetComponent<Button>();
        btn_bag = BtnGroup.Find("Btn_Bag").GetComponent<Button>();
        btn_role = BtnGroup.Find("Btn_Role").GetComponent<Button>();

    }

    public void showSettingPanel()
    {
        btn_setting.onClick.AddListener(() => { settingPanel.SetActive(!settingPanel.activeSelf); });
    }

    public void showMainPanel()
    {

    }

    public void showSkillPanel()
    {
        btn_skill.onClick.AddListener(() => { skillPanel.SetActive(!skillPanel.activeSelf); });
    }
    public void showBagPanel()
    {
        btn_bag.onClick.AddListener(() => { bagPanel.SetActive(!bagPanel.activeSelf); });
    }

    public void showRollPanel(bool isShowRollPanel)
    {
        if (isShowRollPanel)
        {
            //btn_role.onClick.AddListener(() => { rolePanel.SetActive(true); });
            rolePanel.SetActive(true);
            return;
        }
        else
        btn_role.onClick.AddListener(() => { rolePanel.SetActive(!rolePanel.activeSelf); });
        
        
    }

    // Start is called before the first frame update
    void Start()
    {
        rolePanel.SetActive(false);
        bagPanel.SetActive(false);
        settingPanel.SetActive(false);
        skillPanel.SetActive(false);

    }

    // Update is called once per frame
    void Update()
    {

    }
}

运行效果:

UIManager1

四、UIFrame1.0中还是存在的缺点:

1、每个面板的引用都需要单独的写出,项目复杂度高了就不好处理

解决方案:每一个引用都用容器存储起来,要用的时候直接从容器中取出

2、会导致内存的暴涨,统一关闭是效率较差

每一个面板为了在一开始能够被找到,需要一开始的时候全部开启,找到之后再统一关闭,这样会游戏在刚开始运行的时候导致内存的暴涨,统一关闭时效率较差。

解决方案:游戏开始运行的时候不开启所有的面板,而是把每一个面板做成一个预制体,第一次需要用到的时候加载出来,并存进容器,再次用到的时候再从容器中取出来

3、每一个面板需要单独写一个函数,后期的扩展性太差

解决方案:用容器查找的方法来对应到具体的引用,来进行操作

第二部分:UIFrame_2.0

一、新要求:

1、实现每一个UI面板不同的打开关闭效果(平移打开,旋转打开,放大缩小抖动打开等等)

2、依次打开背包面板=>属性面板=》属性面板中的火属性面板,然后点击一个关闭按钮能依次关闭属性面板中的火属性面板=》属性面板=》背包面板

3、改进UIFrame_1.0中的问题,将面板做成预制体加载方式,并使用容器存储起来

4、点击右下角一个图标之后,其余的图标显示灰色禁用状态,并不能被点击

注意点:Unity中读取xml文件时xml应该放置在哪?Assets文件夹下吗?需要用Application.Datapath读取文件吗?

当项目打包之后Assets文件夹会被Unity打包为二进制文件,这样Application.DataPath会找不到Assets文件夹下面的xml文件;应该放在Resources文件夹下。

代码部分:

文件加载模块:加载xml配置文件

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


public static class ResourcesManager
{

    public static Dictionary<string,GameObject> ResourcesDic = new Dictionary<string,GameObject>();
   /// <summary>
   /// 加载资源:加载预制体
   /// 1、判断容器中是否存在该路径,
   /// 2、如果有的话,直接返回GameObj-------------ect
   /// 3、如果没有的话,调用Resources.Load()加载资源,然后再存入在存入到容器中
   /// </summary>
   /// <param name="path"> 传入一个资源路径 </param>
   /// <returns></returns>
    public static GameObject Load(string path)
    {
        if (ResourcesDic.ContainsKey(path))
        {
            return ResourcesDic[path];
        }
        GameObject gobj= Resources.Load(path) as GameObject;
        //ResourcesDic.Add(path, gobj);
        //索引器
        ResourcesDic[path] = gobj;
        return gobj;
    }

    /// <summary>
    /// 重载:加载非预制体的资源
    /// </summary>
    /// <typeparam name="Object"></typeparam>
    /// <param name="path"></param>
    /// <returns></returns>
    /// Hashtable : 非泛型键值对容器,键名和键值都可以是任意的数据类型
    public static Hashtable ResHtb = new Hashtable();
    //泛型函数
    public static T Load<T>(string path) where T : Object
    {
        if (ResHtb.ContainsKey(path))
        {
            //as:只能是引用类型之间的转换,这里必须约束T为引用类型 
            return ResHtb[path] as T ;
        }
        //Resources.Load()方法能返回的东西必须继承自Object:需要保证T可以隐式转换为Object
        T t= Resources.Load<T>(path);
        ResHtb[path] = t;
        return t;
    }
}

MyUIManager: 管理所有UI界面的开启和关闭,相当于mvc中的control层。

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

public enum MyUIEnum
{
    MainPanel=1,
    RolePanel,
    SkillPanel,
    BagPanel,
    SettingPanel
}

/// <summary>
/// 管理所有UI界面的开启和关闭
/// </summary>
public static class MyUIManager 
{
    public static Transform canvas = null;

    public static Transform ThisCanvas
    {
        get 
        {
            if (canvas ==null)
            {
                canvas = GameObject.Find("Canvas").transform;
            }  
            return canvas;  
        }

    }

    //
    private static Dictionary<MyUIEnum, string> prefabDic = new Dictionary<MyUIEnum, string>();

    private static Dictionary<MyUIEnum, BasePanel> panelDic = new Dictionary<MyUIEnum, BasePanel>();

    private static Stack<BasePanel> UIStack = new Stack<BasePanel>();
    /// <summary>
    /// 一开始的时候,从xml中读取所有UI预制体的地址和枚举类型的对应关系
    /// </summary>
    public static void OnloadXml()
    {
        //TextAsset所有外部加载的文本,使用这个数据类型保存
        TextAsset xml = Resources.Load<TextAsset>("Configs/UIPanel");

        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.LoadXml(xml.text);//加载xml文件中的内容
        XmlNodeList list = xmlDoc.SelectSingleNode("root").ChildNodes;

        foreach (XmlElement item in list)
        {   
            MyUIEnum  uiEnum =(MyUIEnum) int.Parse(item.GetAttribute("id"));
            string path ="Prefabs/"+ item.GetAttribute("path");
            prefabDic.Add(uiEnum, path);

        }

    }

    /// <summary>
    /// 根据枚举(编号)拿到对应的ui预制体
    /// </summary>
    /// <param name="uiEnum"></param>
    /// <returns></returns>
    static BasePanel GetPanel(MyUIEnum uiEnum,Transform parent=null)
    {
        if (panelDic.ContainsKey(uiEnum))
        {
            return panelDic[uiEnum];
        }
        //如果这个界面从来没有被打开过
        string path = prefabDic[uiEnum];
        GameObject prefab = ResourcesManager.Load(path);
        GameObject go = GameObject.Instantiate(prefab);//拿到并实例化预制体
        if (parent ==null)
        {
            parent = ThisCanvas;
        }
        go.transform.SetParent(parent);
        BasePanel panel = go.GetComponent<BasePanel>();
        panelDic.Add(uiEnum, panel);
        return panel;
    }

    public static void Load(MyUIEnum uiEnum,Transform parent = null)
    {
        if (UIStack.Count>0)
        {
            BasePanel topPanel = UIStack.Peek();//拿到栈顶元素但不出栈
            topPanel.Pause();
        }
        BasePanel panel = GetPanel(uiEnum,parent);
        UIStack.Push(panel);
        panel.Enter();

    }

    public static void UnLoad()
    {
        if (UIStack.Count<=0)
        {
            return;
        }
        BasePanel topPanel = UIStack.Pop();
        topPanel.Close();
        if (UIStack.Count > 0)
        {
            BasePanel resumePanel = UIStack.Peek();
            resumePanel.Resume();
        }
    }

    public static void Clear()
    {
        canvas = null;
        UIStack.Clear();
    }

}

猜你喜欢

转载自blog.csdn.net/qq_53663718/article/details/127717650
今日推荐