目录
- UI框架介绍
- 保存面板信息
- 解析面板信息,开发配套数据模型
- 开发公共面板类BasePanel
- 开发UIManager核心管理类
- 开发菜单面板类和背包类使用UIManager
- 结束
一,UI框架介绍
为什么要使用UI框架
我们在开发项目中会有许多面板,比如背包面板,技能面板,设置面板等。如此多的面板我们不好统一进行管理,当需求发生更变我们需要快速的做出调整,UI框架就很好的能改善这一点,并且使代码结构更加清晰。
UI框架如何管理
UI框架通过核心类UIManager 1. 解析本地文档 2. 用栈统一控制面板的显示和隐藏。
UI框架UML架构图
UI框架.png二, 保存面板信息
1. 将面板做成预制体保存在本地Resources文件夹下
预制体.png2. 将面板类型及路径写入Json文件
{
"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,一定要标注可序列化特性哦。
[Serializable]
public class UIPanelInfo : ISerializationCallbackReceiver
{
[NonSerialized]
//面板类型
public UIPanelType panelType;
public string panelTypeString;
//面板路径
public string path;
//字符串转化枚举类型
public void OnAfterDeserialize()
{
UIPanelType type = (UIPanelType)System.Enum.Parse(typeof(UIPanelType), panelTypeString);
panelType = type;
}
public void OnBeforeSerialize()
{
}
}
- 解析本地文件,并存入字典方便加载,这里我将其写入核心类UIManager中。
[Serializable]
class UIPanelTypeJson
{
public List<UIPanelInfo> infoList;
}
//解析Json
private void ParseUIPanelTypeJson()
{
panelPathDict = new Dictionary<UIPanelType, string>();
TextAsset ta = Resources.Load<TextAsset>("UIPanelType");
UIPanelTypeJson jsonObject = JsonUtility.FromJson<UIPanelTypeJson>(ta.text);
foreach (UIPanelInfo info in jsonObject.infoList)
{
//Debug.Log(info.panelType);
panelPathDict.Add(info.panelType, info.path);
}
}
小提示:在Unity 5.3中Unity开放了一个JsonUnility序列化类详情请自查API。
作用: 1. 目前支持Json转化为对象。
2. 对象转化为Json格式字符。
四,开发公共面板类BasePanel
所有面板都有界面的显示,暂停。像我们的菜单面板还需要有暂停和继续,当其他面板显示我们可以防止用户与其他面板继续交互。当当前面板处于隐藏我们才允许菜单面板与用户继续交互。所以我们将面板都具有的共性做成一个公共的基类。
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()
{
}
}
五,开发UIManager核心管理类
当你看到这里,恭喜你到了最核心的一部分了。
- 扩展Dictionary类获取Value,如果取不到则返回空。
- 准备一个字典成员用来存储路径和面板实例,方便获取面板。
- 准备一个数据结构栈控制面板的显示和隐藏。
- 书写实例化面板逻辑的成员函数。
定义一个根据面板类型得到实例化面板
/// <summary>
/// 根据面板类型 得到实例化的面板
/// </summary>
/// <returns></returns>
private BasePanel GetPanel(UIPanelType panelType)
{
if (panelDict == null)
{
panelDict = new Dictionary<UIPanelType, BasePanel>();
}
BasePanel panel = panelDict.TryGet(panelType);
if (panel == null)
{
//如果找不到,那么就找这个面板的prefab的路径,然后去根据prefab去实例化面板
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>
/// 把某个页面入栈, 把某个页面显示在界面上
/// </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);
Debug.Log(panel == null);
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();
}
六,开发菜单面板类和背包类使用UIManager
上面所述我们已经完成大部分UI框架的核心架构了,接下来根据个人项目来使用该框架,这里我将举个例子来使用UI框架。
开发菜单面板类
我们先将我们的面板继承公共基类BasePanel,其次这里我将介绍在菜单面板类重写暂停,重启,以及Button监听事件逻辑。
/// <summary>
/// 界面暂停
/// </summary>
public override void OnPause()
{
canvasGroup.blocksRaycasts = false;//当弹出新的面板的时候,让主菜单面板 不再和鼠标交互
}
/// <summary>
/// 界面重启
/// </summary>
public override void OnResume()
{
canvasGroup.blocksRaycasts = true;
}
/// <summary>
/// 启动对应面板主要用于监听事件
/// </summary>
/// <param name="panelTypeString"></param>
public void OnPushPanel(string panelTypeString)
{
UIPanelType panelType = (UIPanelType)System.Enum.Parse(typeof(UIPanelType), panelTypeString);
UIManager.Instance.PushPanel(panelType);
}
接下来我们再开发一个背包面板,来测试UI框架,这里背包面板主要重写显示和隐藏逻辑,暂停和重启逻辑与菜单面板相似。
/// <summary>
/// 显示背包
/// </summary>
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);
}
/// <summary>
/// 隐藏背包
/// </summary>
public override void OnExit()
{
//canvasGroup.alpha = 0;
canvasGroup.blocksRaycasts = false;
transform.DOLocalMoveX(600, .5f).OnComplete(() => canvasGroup.alpha = 0);
}
最后我们将菜单面板的Button注册监听事件OnPushPanel,分配面板类型字符串,即可开启测试。
七. 结束
感谢观看,希望这篇文章对你真正有所感悟。
网友评论