1、什么是UI框架
主要功能:管理场景中所有的面板;控制面板之间的跳转
2、简要描述UI框架的思路,步骤如下
1、创建UIPanelType来表示UI面板的类型,保存现在这个工程所有的面板(比如此背包面板,商城面板,主菜单等)
2、创建UIPanelType.json保存所有panel和它对应的路径。所有的panel都存放在Resources文件夹中,以方便用到的时候加载,如图
json文件里面的UIPanle信息用一个数组保存(可以在浏览器搜过json在线校验就能校验格式是否正确)
3、创建UIPanelInfo类来实现程序于json的连接。用这个类来对应UIPanelJson,以此类来操作Json里面的数据
4、创建UIManager来管理所有的面板(UI框架的核心管理)实现的功能包括解析保存所有面板的信息(PanelPathDict);创建保存所有面板的实例(panelDict);管理保存所有显示的面板。除此之外,可以新增新功能
5、创建所面板的基类BasePanel来实现所有面板共同的功能的方法,比如界面开启,界面继续,界面关闭等(做成虚方法,让子类去实现)
6、创建GameRoot类来启动UI框,因为只用这个类挂载在游戏物体上面的(Canvas),所以只有这个类需要继承MonoBehaviour。
总结:
3、项目主要功能介绍
1、简单说明:实现主菜单,背包界面,商城界面的跳转等
2、具体功能实现
1、UIPanelType,用枚举类型来定义面板
2、 UI面板信息类UIPanelInfo
功能是:用来对应Json的文件信息
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//用这个类来对应UIPanelJson,以此类来操作Json里面的数据
[Serializable]
public class UIPanelInfo : ISerializationCallbackReceiver
{
//与json里面名称对应起来
[NonSerialized]
public UIPanelType panelType;//Json无法解析这个类型的数据
//将string类型的数据转化为上面枚举类型的数据
public string panelTypeString;
/* {
get
{
//序列化
return panelType.ToString();
}
set
{
Debug.Log(value);
//反序列化
UIPanelType type = (UIPanelType)System.Enum.Parse(typeof(UIPanelType), value);
panelType = type;
}
}*/
public string path;
//反序列化 从文本信息 到对象
public void OnAfterDeserialize()
{
UIPanelType type = (UIPanelType)System.Enum.Parse(typeof(UIPanelType), panelTypeString);
panelType = type;
}
public void OnBeforeSerialize()
{
throw new NotImplementedException();
}
}
3、 UIManager设置为单例模式
(1)关于面板跳转的实现的说明:在游戏运行开始,实例化所需要的面板,用Stack来存储所有的实例化的面板,push代表实例化,pop代表关闭面板。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//UI框架的核心管理类
public class UIManager
{
#region [UImanager单例的创建]
private static UIManager _instance;
/// <summary>
/// 单例模式的核心
///1、定义一个静态的对象
///2、构造方法私有化
/// </summary>
//在外部就可以通过这个静态方法访问
public static UIManager Instance
{
get
{
if (_instance == null)
{
_instance = new UIManager();
}
return _instance;
}
}
//私有的让外接无法实例化本对象(构造方法)
private UIManager()
{
//实例一创建就开始解析json文件
ParseUIPanelTypeJson();
}
#endregion
private Transform canvasTransform;//用来设置canvase和panel的父子关系
private Transform CanvasTransform
{
get
{
if (canvasTransform == null)
{
canvasTransform = GameObject.Find("Canvas").transform;
}
return canvasTransform;
}
}
private Dictionary<UIPanelType, string> panelPathDict;//存储所有的面板prafab的路径
private Dictionary<UIPanelType, BasePanel> panelDict;//保存所有实例化面板的游戏物体身上的BasePanel组件
private Stack<BasePanel> basePanelStack;
#region 1、解析保存所有面板信息
[Serializable]
class UIPanelTypeJson
{
public List<UIPanelInfo> infoList;
}
//解析json文件
private void ParseUIPanelTypeJson()
{
panelPathDict = new Dictionary<UIPanelType, string>();
TextAsset ta = Resources.Load<TextAsset>("UIPanelType");
//解析json文件(读取json里面的所有信息)
// JsonUtility.FromJson可以生成一个对象
UIPanelTypeJson jsonObject = JsonUtility.FromJson<UIPanelTypeJson>(ta.text);
//通过jsonObject对象访问里面的数据即infoList
foreach (UIPanelInfo info in jsonObject.infoList)
{
panelPathDict.Add(info.panelType, info.path);
}
}
#endregion
#region 2、创建保存所有面板实例
/// <summary>
/// 根据面板的类型,得到实例化的面板,再存到字典里面,下次就不用再多次实例化对象了
/// </summary>
/// <returns></returns>
private BasePanel GetPanel(UIPanelType panelType)
{
if (panelDict == null)
{
panelDict = new Dictionary<UIPanelType, BasePanel>();
}
/* BasePanel basePanel;
panelDict.TryGetValue(panelType, out basePanel);//TODO*/
BasePanel basePanel = panelDict.TryGet(panelType);
//如果找不到,就找这个面板的prefab的路径,拿到这个路径创建它的实例
if (basePanel == null)
{
//得到路径
/*string path;
panelPathDict.TryGetValue(panelType, out path);*/
string path = panelPathDict.TryGet(panelType);
//实例化加载的面板prefab
GameObject instPanel = GameObject.Instantiate(Resources.Load(path)) as GameObject;
//设置父物体
instPanel.transform.SetParent(CanvasTransform, false);//false保持局部位置而不保持世界位置,要不然面板位置和scal将发生改变
panelDict.Add(panelType, instPanel.GetComponent<BasePanel>());
return instPanel.GetComponent<BasePanel>();
}
else
{
return basePanel;
}
}
#endregion
#region 3、管理保存所有显示页面的
//把某个页面入栈,把某个页面显示在界面上
public void PushPanel(UIPanelType panelType)
{
if (basePanelStack == null) basePanelStack = new Stack<BasePanel>();
//判断一下stack里面是否有页面
if(basePanelStack.Count > 0)
{
//取出栈顶的页面然后暂停它
basePanelStack.Peek().OnPause();
}
//把新的界面开始并且入栈
BasePanel basePanel = GetPanel(panelType);
basePanel.OnEnter();
basePanelStack.Push(basePanel);
}
//出栈,把页面从界面上移除
public void PopPanel()
{
if (basePanelStack == null) basePanelStack = new Stack<BasePanel>();
if (basePanelStack.Count <= 0) return;
//关闭栈顶界面的显示
basePanelStack.Pop().OnExit();
//显示栈顶下面的界面
if (basePanelStack.Count <= 0)
{
//没有界面了
return;
}
//显示下一个界面
basePanelStack.Peek().OnResume();
}
#endregion
/// <summary>
/// 方法test
/// </summary>
public void Test()
{
string path;
panelPathDict.TryGetValue(UIPanelType.Knapsack, out path);
Debug.Log(path);
}
}
4、Basepanel
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BasePanel : MonoBehaviour
{
//界面开启
public virtual void OnEnter()
{
}
//界面暂停
public virtual void OnPause()
{
}
//界面继续
public virtual void OnResume()
{
}
//界面不显示,退出这个界面,界面被关闭
public virtual void OnExit()
{
}
}
5、以下就是各个功能面板的基本功能,此处介绍其一,其他的类似,并且之后可以在每个面板拓展新功能
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
public class KnapsackPanel : BasePanel
{
CanvasGroup canvasGroup;
private void Awake()
{
canvasGroup = GetComponent<CanvasGroup>();
}
public void OnClosePanel()
{
UIManager.Instance.PopPanel();
}
public override void OnEnter()
{
canvasGroup.alpha = 1;
canvasGroup.blocksRaycasts = true;
//先设置背包面板在屏幕外面
Vector3 temp = transform.localPosition;
temp.x = 600;
transform.localPosition = temp;
//DoTween动画
transform.DOLocalMoveX(0, 0.5f);
}
public override void OnExit()
{
//canvasGroup.alpha = 0;//隐藏
canvasGroup.blocksRaycasts = false;//无法交互
transform.DOLocalMoveX(600, .5f).OnComplete(() => canvasGroup.alpha = 0);
}
//处理Slot弹出ItemInfo
public void OnItemButtonclick()
{
UIManager.Instance.PushPanel(UIPanelType.ItemMessage);
}
//打开ItemMessage时需要用到
public override void OnPause()
{
canvasGroup.blocksRaycasts = false;//无法交互
}
//关闭ItemMessage时开启交互
public override void OnResume()
{
canvasGroup.blocksRaycasts = true;
}
}
4、新知识的学习
1、c#扩展类的应用,比如此项目里用到字典来存储面板及面板路径(PanelPathDict和panelDict)因为这个项目里面用了两个字典来存储面板信息,所以创建一个拓展方法来节省重复代码的使用。
功能就是根据传入的键来取得相应的值。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public static class DictionaryExtension //扩展字典类的方法
{
/// <summary>
/// 尝试根据key得到value,得到了就直接返回,没有得到直接返回null
/// </summary>
/// <typeparam name="Tkey">字典key的参数类型</typeparam>
/// <typeparam name="Tvalue">字典值的参数类型</typeparam>
/// <param name="dict">表示我们要获取值的字典</param>
/// <param name=""></param>
public static Tvalue TryGet<Tkey, Tvalue>(this Dictionary<Tkey, Tvalue> dict, Tkey key)
{
Tvalue value;
dict.TryGetValue(key, out value);
return value;
}
}