了解UI框架

Unity3D之搭建简易有效的UI框架

  • 什么是UI框架

Unity3D之搭建简易有效的UI框架_胖胖的橘猫君-CSDN博客_u3d ui框架

  UI框架用于管理场景中的所有面板,控制面板之间的切换,可以加快开发进度、提高代码质量。

二、实现思路

根据用户界面调用情况,分析有如下四种状态:

进入状态:界面第一次被动态加载使用的时候

暂停状态:切换到其他界面的时候

继续状态:重新回到界面的时候

退出状态:界面不显示的时候

实现步骤: 

1、使用JSON保存面板路径,枚举保存面板类型

2、根据界面共有的四种状态,创建UI基类BasePanel,场景中的界面继承该基类,并将四种状态写成虚方法,依次分别为OnEnter(),OnPause(),OnResume(),OnExit(),提供给子类重写。

3、通过管理类UIManager,解析JSON,管理UI界面的加载和切换。为了方便调用,做成单例模式。分别用两个字典保存从JSON读取的面板信息和已动态加载实例化的面板。通过栈来管理场景中所有面板之间的切换。

缺点也比较明显,因为用栈来存储场景中依次打开的界面,也只能依次从栈顶界面开始关闭。

 页面状态流程图如下所示:

 

 

UI框架类图如下:

 

三、代码实现

 UI界面基类

 BasePanel.cs

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.cs

public class UIManager

{

    // 单例模式:定义一个静态的对象,构造方法私有化,内部构造,用于外部访问

    private static UIManager _instance;

    public static UIManager Instance

    {

        get

        {

            if (_instance == null)

            {

                _instance = new UIManager();

            }

            return _instance;

        }

    }

    //字典存储所有面板的Prefabs路径

    private Dictionary<UIPanelType, string> panelPathDict = new Dictionary<UIPanelType, string>();

    //保存所有已实例化面板的游戏物体身上的BasePanel组件

    private Dictionary<UIPanelType, BasePanel> panelDict = new Dictionary<UIPanelType, BasePanel>();

    //存储当前场景中的界面

    private Stack<BasePanel> panelStack = new Stack<BasePanel>();

    private Transform canvasTransform;

    private Transform CanvasTransform

    {

        get

        {

            if (canvasTransform == null)

            {

                canvasTransform = GameObject.Find("Canvas").transform;

            }

            return canvasTransform;

        }

    }

    private UIManager()

    {

        ParseUIPanelTypeJson();

    }

    /// <summary>

    /// 解析JSON,获取所有面板的路径信息

    /// </summary>

    private void ParseUIPanelTypeJson()

    {

        TextAsset ta = Resources.Load<TextAsset>("UIPanelType");

        JsonData jsonDataArray = JsonMapper.ToObject(ta.text);

        foreach (JsonData item in jsonDataArray)

        {

            UIPanelType panelType = (UIPanelType)Enum.Parse(typeof(UIPanelType), item["panelType"].ToString());

            string path = item["path"].ToString();

            panelPathDict.Add(panelType, path);

        }

    }

    /// <summary>

    /// 根据面板类型,返回对应的BasePanel组件

    /// </summary>

    /// <param name="panelType">需要返回的面板类型</param>

    /// <returns>返回该面板组件</returns>

    private BasePanel GetPanel(UIPanelType panelType)

    {

        BasePanel basePanel = panelDict.GetValue(panelType);

        //如果panel为空,根据该面板prefab的路径,实例化该面板

        if (basePanel == null)

        {

            string path = panelPathDict.GetValue(panelType);

            GameObject newPanel = GameObject.Instantiate(Resources.Load<GameObject>(path)) as GameObject;

            newPanel.transform.SetParent(CanvasTransform, false);

            //第一次实例化的面板需要保存在字典中

            panelDict.Add(panelType, newPanel.GetComponent<BasePanel>());

            return newPanel.GetComponent<BasePanel>();

        }

        else

        {

            return basePanel;

        }

    }

    /// <summary>

    /// 设置默认的栈顶元素

    /// </summary>

    /// <param name="panelType">界面类型</param>

    /// <param name="basePanel">组件</param>

    public void SetDefaultPopPanel(UIPanelType panelType,BasePanel basePanel)  

    {

        panelDict.Add(panelType, basePanel);

        panelStack.Push(basePanel);

    }

    /// <summary>

    /// 把该页面显示在场景中

    /// </summary>

    /// <param name="panelType">需要显示界面的类型</param>

    public void PushPanel(UIPanelType panelType)

    {

        //判断一下栈里面是否有页面

        if (panelStack.Count > 0)

        {

            panelStack.Peek().OnPause();//原栈顶界面暂停

        }

        BasePanel panel = GetPanel(panelType);

        panel.OnEnter();//调用进入动作

        panelStack.Push(panel);//页面入栈

    }

    /// <summary>

    /// 关闭栈顶界面显示

    /// </summary>

    public void PopPanel()

    {

        //当前栈内为空,则直接返回

        if (panelStack.Count <= 0) return;

        panelStack.Pop().OnExit();//Pop删除栈顶元素,并关闭栈顶界面的显示,

        if (panelStack.Count <= 0) return;

        panelStack.Peek().OnResume();//获取现在栈顶界面,并调用界面恢复动作

    }

}

四、案例讲解

 点击主菜单按钮,实现各个面板之间的切换效果,源文件在末尾。

 效果展示如下:

 将制作好的面板做成预制体放在Resources文件中,如下所示:

 

 将状态面板类型记录在枚举中,如下所示:

public enum UIPanelType

{

    ItemMessagePanel,

    CharacterPanel,

    KnapsackPanel,

    MainMenuPanel,

    ShopPanel,

    SkillPanel,

    SystemPanel,

    TaskPanel

}

 根据面板和其路径,记录在JSON中,如下所示:

 

[

    {

        "panelType": "ItemMessagePanel",

        "path": "UIPanel/ItemMessagePanel"

    },

    {

        "panelType": "CharacterPanel",

        "path": "UIPanel/CharacterPanel"

    },

    {

        "panelType": "KnapsackPanel",

        "path": "UIPanel/KnapsackPanel"

    },

    {

        "panelType": "ShopPanel",

        "path": "UIPanel/ShopPanel"

    },

    {

        "panelType": "SkillPanel",

        "path": "UIPanel/SkillPanel"

    },

    {

        "panelType": "SystemPanel",

        "path": "UIPanel/SystemPanel"

    },

    {

        "panelType": "TaskPanel",

        "path": "UIPanel/TaskPanel"

    }

]

 各个界面分别创建BasePanel的子类,用于实现界面的四种状态。为了方便实现界面的动画,使用DOTween插件,如果不会的话,可以看我之前的学习博客。 https://blog.csdn.net/qq_35361471/article/details/79353071

为各个界面添加CanvasGroup组件,如下:

 

 Alpha可以方便实现隐藏和显示,取消勾选Blocks Raycasts,可以实现屏蔽UI射线检测

 主菜单界面需要实现一个提供给各个界面按钮的方法接口,如下:

public void OnPushPanel(string panelTypeString)

 {

        UIPanelType panelType = (UIPanelType)System.Enum.Parse(typeof(UIPanelType), panelTypeString);

        UIManager.Instance.PushPanel(panelType);

}

 给对应按钮添加监听方法的时候,需要注意,这里的字符串要和枚举UIPanelType里的值写的一致。

 

 以CharacterPanel.cs为例,继承BasePanel,并重写四种方法,使用DOTween实现动画,代码如下:

using UnityEngine;

using DG.Tweening;

public class CharacterPanel : BasePanel

{

    private CanvasGroup canvasGroup;

    void Start()

    {

        if (canvasGroup == null) canvasGroup = GetComponent<CanvasGroup>();

    }

    public override void OnEnter()

    {

        if (canvasGroup == null) canvasGroup = GetComponent<CanvasGroup>();

        canvasGroup.alpha = 1;

        canvasGroup.blocksRaycasts = true;

        Vector3 temp = transform.localPosition;

        temp.x = -800;

        transform.localPosition = temp;

        transform.DOLocalMoveX(0, 0.5f);

    }

    public override void OnPause()

    {

        canvasGroup.blocksRaycasts = false;

    }

    public override void OnResume()

    {

        canvasGroup.blocksRaycasts = true;

    }

    public override void OnExit()

    {

        transform.DOLocalMoveX(-800, .5f).OnComplete(() => canvasGroup.alpha = 0);

    }

    public void OnClosePanel()

    {

        UIManager.Instance.PopPanel();

    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值