UI框架-UIManager

C#

浏览数:83

2019-9-1

AD:资源代下载服务

目录

  1. UI框架介绍
  2. 保存面板信息
  3. 解析面板信息,开发配套数据模型
  4. 开发公共面板类BasePanel
  5. 开发UIManager核心管理类
  6. 开发菜单面板类和背包类使用UIManager
  7. 结束

一,UI框架介绍

为什么要使用UI框架

我们在开发项目中会有许多面板,比如背包面板,技能面板,设置面板等。如此多的面板我们不好统一进行管理,当需求发生更变我们需要快速的做出调整,UI框架就很好的能改善这一点,并且使代码结构更加清晰。

UI框架如何管理

UI框架通过核心类UIManager 1. 解析本地文档 2. 用栈统一控制面板的显示和隐藏。

UI框架UML架构图

UI框架.png

二, 保存面板信息

1. 将面板做成预制体保存在本地Resources文件夹下

预制体.png

2. 将面板类型及路径写入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"}

]
}

三, 解析面板信息,开发配套数据模型

通过上面操作我们已经将我们的面板保存到本地了,当我们使用的时候只需要解析本地文件并做数据存储即可。
  1. 准备我们的数据模型类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()
    {

    }
}
  1. 解析本地文件,并存入字典方便加载,这里我将其写入核心类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核心管理类

当你看到这里,恭喜你到了最核心的一部分了。
  1. 扩展Dictionary类获取Value,如果取不到则返回空。
  2. 准备一个字典成员用来存储路径和面板实例,方便获取面板。
  3. 准备一个数据结构栈控制面板的显示和隐藏。
  4. 书写实例化面板逻辑的成员函数。
定义一个根据面板类型得到实例化面板
  /// <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,分配面板类型字符串,即可开启测试。

七. 结束

感谢观看,希望这篇文章对你真正有所感悟。

作者:一云小窝