首先
想想我为什么要用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()
{
}
}//类
}//命名空间