首先我们需要先定义这么一个UIManager类。
public class UIManager
{
}
UI管理器嘛,顾名思义肯定是用来管理我们游戏中的UI的,而我们游戏当中的UI呢一般是以面板为单位来进行划分的。所以我们还需要一个UI面板类。然后通过我们的UI管理器来管理我们的UI面板。
public class UIPanel
{
}
那么我们在UI管理器当中呢使用字典去管理我们的UI面板。
public class UIManager
{
Dictionary<string, UIPanel> panelsDic;
public UIManager()
{
panelsDic = new Dictionary<string, UIPanel>();
}
}
那么我们的UI面板当中有几个最基本的属性
比如最基本的声明周期函数,OnShow,OnHide,当面板被打开和隐藏时去执行的函数。以及这个面板所对应的预制体的资源路径或名称。
public abstract class UIPanel
{
private string path;
public string Path { get { return path; } }
public GameObject root;
public abstract void OnShow();
public abstract void OnHide();
public UIPanel(string path)
{
this.path = path;
}
}
首先呢这个面板类是一个抽象类,我们的登录面板或者注册面板等等的UI面板都需要去继承他,所以这个面板类本身是不应该可以实例化的,因此是一个抽象类,然后是path,也就是这个UI面板所对应的预制体的资源路径,当我们通过UI管理器去打开这个面板的时候需要根据这个路径去加载面板资源并拿到这个资源的引用然后赋值给root,也就是这个预制体的根节点,然后我们这个脚本才可以去对这个物体进行操作.然后在打开和隐藏的时候我们会分别去调用OnShow和OnHide方法,而这两个方法则是有子类去书写具体实现的.
那么接下来我们来写UIManager.
public class UIManager
{
Transform canvas;
Dictionary<string, UIPanel> panelsDic;
public UIManager()
{
canvas = GameObject.Find("Canvas").transform;
GameObject eventSystem = GameObject.Find("EventSystem");
GameObject.DontDestroyOnLoad(canvas);
GameObject.DontDestroyOnLoad(eventSystem);
panelsDic = new Dictionary<string, UIPanel>();
}
}
刚才说了,我们使用一个字典来存储所有的UI面板.
然后我们还应该在unity界面创建一个Canvas和EventSystem,这两个物体,前者是所有UI面板的父节点,后者是检测UI事件的,这两者都是必须存在的.
然后还应该将这两个物体都设置为不被销毁的,以防止在切换场景的时候被销毁掉.
然后我们还应该提供打开面板和关闭面板两个最基本的操作.
public class UIManager
{
Transform canvas;
Dictionary<string, UIPanel> panelsDic;
public UIManager()
{
canvas = GameObject.Find("Canvas").transform;
GameObject eventSystem = GameObject.Find("EventSystem");
GameObject.DontDestroyOnLoad(canvas);
GameObject.DontDestroyOnLoad(eventSystem);
panelsDic = new Dictionary<string, UIPanel>();
}
public void OpenPanel<T>(T panel) where T : UIPanel
{
}
public void ClosePanel<T>(T panel) where T : UIPanel
{
}
}
在这里我们对关闭和打开面板都提供一个泛型接口,传递面板传递进来,但是我们如果每次都要将这个面板对象都传进来就太麻烦了,所以我们再次对面板类进行扩展.
public abstract class UIPanel
{
private string path;
public string Path { get { return path; } }
public GameObject root;
public abstract void OnShow();
public abstract void OnHide();
public void OpenPanel() => Singleton<UIManager>.Instance.OpenPanel(this);
public void ClosePanel() => Singleton<UIManager>.Instance.ClosePanel(this);
public UIPanel(string path)
{
this.path = path;
}
}
那么这时候我们就不需要通过UI管理器去打开或者隐藏面板了,而是直接调用这个对象的方法.
接下来讲一下这两个方法的具体实现.
public class UIManager
{
Transform canvas;
Dictionary<string, UIPanel> panelsDic;
public UIManager()
{
canvas = GameObject.Find("Canvas").transform;
GameObject eventSystem = GameObject.Find("EventSystem");
GameObject.DontDestroyOnLoad(canvas);
GameObject.DontDestroyOnLoad(eventSystem);
panelsDic = new Dictionary<string, UIPanel>();
}
public void OpenPanel<T>(T panel) where T : UIPanel
{
if(panelsDic.TryGetValue(typeof(T).Name,out UIPanel oldPanel))
{
oldPanel.root.SetActive(true);
oldPanel.OnShow();
}
else
{
GameObject newGO = Singleton<ResourcesManager>.Instance.Load<GameObject>(panel.Path);
panel.root = newGO;
panel.root.name = panel.Path;
panel.OnShow();
newGO.transform.SetParent(canvas);
panelsDic.Add(typeof(T).Name,panel);
}
}
public void ClosePanel<T>(T panel) where T : UIPanel
{
if(panelsDic.TryGetValue(typeof(T).Name,out UIPanel oldPanel))
{
oldPanel.root.SetActive(false);
}
}
}
其实很简单,打开面板我们这里的实现是先检查字典当中是否存在该面板,如果存在就直接显示出来,并且调用他的OnShow方法,如果不存在,则从资源管理器中去加载这个资源,然后调用他的OnShow方法.关闭就更简单了,判断是否存在于该字典当中,存在的话就隐藏,不存在的话,.....那就是逻辑有问题了,因为我们每次打开都是有添加进字典的,这里可以捕捉下错误.也许是误操作.
最后我在把这整个的代码完整的发一下,因为用到了前两篇的内容.
public class Singleton<T> where T : new()
{
static T instance;
public static T Instance
{
get
{
if (instance == null)
instance = new T();
return instance;
}
}
}
public class ResourcesManager
{
private Dictionary<string, IResource> ressDic;
public ResourcesManager()
{
ressDic = new Dictionary<string, IResource>();
}
public T Load<T>(string path) where T : UnityEngine.Object
{
if (ressDic.TryGetValue(path, out IResource iRes))
return (iRes as Resource<T>).Res;
T res = Resources.Load<T>(path);
ressDic.Add(path, new Resource<T>(path, res));
return res;
}
}
public interface IResource
{
}
public class Resource<T> : IResource
{
public Resource(string path, T res)
{
this.path = path;
this.res = res;
}
private string path;
private T res;
public string Path { get; }
public T Res { get; }
}
public class UIManager
{
Transform canvas;
Dictionary<string, UIPanel> panelsDic;
public UIManager()
{
canvas = GameObject.Find("Canvas").transform;
GameObject eventSystem = GameObject.Find("EventSystem");
GameObject.DontDestroyOnLoad(canvas);
GameObject.DontDestroyOnLoad(eventSystem);
panelsDic = new Dictionary<string, UIPanel>();
}
public void OpenPanel<T>(T panel) where T : UIPanel
{
if(panelsDic.TryGetValue(typeof(T).Name,out UIPanel oldPanel))
{
oldPanel.root.SetActive(true);
oldPanel.OnShow();
}
else
{
GameObject newGO = Singleton<ResourcesManager>.Instance.Load<GameObject>(panel.Path);
panel.root = newGO;
panel.root.name = panel.Path;
panel.OnShow();
newGO.transform.SetParent(canvas);
panelsDic.Add(typeof(T).Name,panel);
}
}
public void ClosePanel<T>(T panel) where T : UIPanel
{
if(panelsDic.TryGetValue(typeof(T).Name,out UIPanel oldPanel))
{
oldPanel.root.SetActive(false);
}
}
}
public abstract class UIPanel
{
private string path;
public string Path { get { return path; } }
public GameObject root;
public abstract void OnShow();
public abstract void OnHide();
public void OpenPanel() => Singleton<UIManager>.Instance.OpenPanel(this);
public void ClosePanel() => Singleton<UIManager>.Instance.ClosePanel(this);
public UIPanel(string path)
{
this.path = path;
}
}
作者:引擎猫 https://www.bilibili.com/read/cv12007091?spm_id_from=333.999.0.0 出处:bilibili