目录
第一部分: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();
}
}