简单的Unity UI框架(基于UGUI)

本文介绍了如何构建一个简单的Unity UI框架,利用单例模式的UIManager管理UI预制体,通过Dictionary和Stack存储和控制UI的显示与关闭。UIPanel作为UI组件的父类,提供初始化、重新打开和关闭的回调。UI面板的具体实现继承自UIPanel,可以自定义动画效果。此外,讨论了两种实现方式,包括使用Json文件存储UI信息和直接在Unity界面挂载UI预制体。最后提醒注意该框架可能不适用于复杂的UI交互,如背包系统,并未处理下层UI的点击响应问题。

基本类和思路

UIManager: 使用单例模式,有一个Dictionary<string,GameObject>存储具体UI预制体的引用,两个
Stack<>分别存储当前显示的UI和被关闭的UI(以便再次打开)。同时可以做到逻辑清晰的UI叠加显示。
同时有打开UI,关闭UI,清空栈的方法(切换场景前使用)。

using System.Collections.Generic;
using UnityEngine;

public class UIManager : MonoBehaviour
{
    //存储UIPanel引用
    private Dictionary<string, GameObject> panelGroup;

    //显示的UI面板
    private Stack<GameObject> currentPanel;

    //被关闭的UI
    private Stack<GameObject> hidePanel;

    private UIManager()
    {
        panelGroup = new Dictionary<string, GameObject>();
        hidePanel = new Stack<GameObject>();
        currentPanel = new Stack<GameObject>();
    }

    private static readonly UIManager _instance = new UIManager();
    //private static readonly object padlock = new object();

    public static UIManager Instance
    {
        get
        {
            //第一次没有读取的情况
            if (_instance.panelGroup.Count == 0)
            {
                _instance.ReadJsonInfo();
            }
            return _instance;
        }
        set { }
    }

    public void OpenUI(string name)
    {
        if (panelGroup.ContainsKey(name))
        {
            if (hidePanel.Count > 0 && hidePanel.Peek().name == panelGroup[name].name + "(Clone)")
            {
                currentPanel.Push(hidePanel.Pop());
                currentPanel.Peek().GetComponent<UIPanel>().ReOpen();
            }
            else
            {
                currentPanel.Push(GameObject.Instantiate(panelGroup[name], FindObjectOfType<Canvas>().transform));
                currentPanel.Peek().GetComponent<UIPanel>().OnInit();
            }
        }
        else
        {
            Debug.LogError("UI不存在");
        }

        ShowStack();
    }

    public void CloseUI()
    {
        currentPanel.Peek().GetComponent<UIPanel>().OnClose();
        hidePanel.Push(currentPanel.Pop());
        ShowStack();
    }

    public void ClearStack()
    {
        hidePanel.Clear();
        currentPanel.Clear();
    }

    public void ReadJsonInfo()
    {
        //Debug.Log("加载Json");
        TextAsset uiInfo = Resources.Load<TextAsset>("UIInfo");
        Intermediary info = JsonUtility.FromJson<Intermediary>(uiInfo.text);
        string[] uiNames = info.names.ToArray();
        //Debug.Log(uiNames.Length);
        GameObject temp = null;
        for (int i = 1; i < uiNames.Length; i++)
        {
            temp = Resources.Load(uiNames[0] + uiNames[i]) as GameObject;
            if (temp == null) Debug.LogError("UI读取失败");
            else
            {
                panelGroup.Add(uiNames[i], temp);
                //Debug.Log(temp.name);
            }
        }
    }
	//测试用,显示栈中内容
    private void ShowStack()
    {
        GameObject[] temp = currentPanel.ToArray();
        for (int i = 0; i < temp.Length; i++)
        {
            Debug.Log("currentPanel:" + temp[i].name);
        }

        temp = hidePanel.ToArray();
        for (int i = 0; i < temp.Length; i++)
        {
            Debug.Log("hidePanel:" + temp[i].name);
        }
    }
    
    private class Intermediary
    {
        public List<string> names;
    }
}

UIPanel: 因为UIManager不在Inspector面板实例化,所以在UIPanel的方法里调用UIManager的方法,方便在UI组件面板上手动挂载方法 ,同时有OnInit(),Reopen(),OnClose()等虚方法(UIManager会在控制出入栈时调用),作为具体uiPanel脚本的父类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UIPanel : MonoBehaviour
{
    public virtual void OnInit() { }

    public virtual void OnClose() { gameObject.SetActive(false); }

    public virtual void ReOpen() { gameObject.SetActive(true); }

    //UI事件函数:关闭当前UI,打开新UI,切换场景
    public void Open(string name) => UIManager.Instance.OpenUI(name);

    public void Close() => UIManager.Instance.CloseUI();

    public void SwitchScene() => UIManager.Instance.ClearStack();

    public void Exit() => Application.Quit();
}

具体的UI: 继承自UIPanel,可以重写UIPanel的虚方法在开启关闭时做出一些动画效果,同时写对应UI需要实现的方法。

Json文件: 保存UI预制体的地址和名字,方便移植(第一个是地址,下面的是名字)

{
"names":
["UIPrefabs\\",
"StartUI",
"ModeChooseUI",
"SettingUI",
"HelpUI",
"EndlessModeUI",
"LevelChooseUI",
"ColorModeUI",
"SimpleModeUI",
"EndUI",
"RiskModeUI",
"SkinChooseUI",
"SuccessUI",
"StopUI"]
}

其他可行的方法

  • 不使用Json文件,在第一个场景中挂载了UIManager的物体,在Awake()里写DontDestoryOnLoad()。然后给UIManager一个public数组,在unity界面里直接挂载需要的UI预制体。省去了读取Json的步骤,但是需要解决重新回到第一个场景时UIManager重复的问题。

注意

这里只是一个简单的思路,用来一些简单的加载,关闭UI,没用试过是否能和背包等复杂一点的UI结合,并且这些代码里没有屏蔽层叠UI时下层的点击,可以在向currentPanel入栈前,使用peek()取出第一个UI关闭点击响应。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值