基本类和思路

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

被折叠的 条评论
为什么被折叠?



