一个通用的UGUI小框架就算是写完了,下面是一步步的思考与优化过程
从使用者的角度来整理一下可能会发出的疑问
0. PanelManager.Instance.LoadPanel<TestPanel>("AnyWnd");
其中TestPanel和AnyWnd都代表了什么?
AnyWnd:面板预制体
TestPanel:面板预制体的脚本
我特意将二者分开就是让你理清思路,知道加载的是什么东西
其实二者可以合二为一,只需要让脚本名和预制体名一致,再修改LoadPanel中传入的名字即可
1.面板管理类的单例是如何创建的面板?
是通过对面板基类的封装,直接F12一步一步地向上阅读即可明白
2.为什么首次调用要清空面板字典?可不可以不清除?
防止重复添加,确保数据一致,避免内存泄漏
可以不清除,关键是要确保字典中的数据始终是有效且一致
3.是不是还可以继续优化?
是的,比如采用异步加载的方式?
比如通过代码设置面板的层级?
比如在面板子类中对UI的显示隐藏的动画修改?
因为我将面板基类写成了虚函数,所以你可以对其在子类中修改加载打开和关闭
甚至直接修改面板基类也没什么不可能的
面板管理类
csusing System.Collections; using System.Collections.Generic; using Unity.VisualScripting; using UnityEngine; using UnityEngine.UIElements; public class PanelManager:MonoBehaviour { private Transform canvasPos;//场景中的Canvas位置 /// <summary> /// string =子类面板名 /// BasePanel 里氏替换原则,所有继承了面板基类的子类面板都可以用父类容器装 /// </summary> private Dictionary<string, BasePanel> panels;//存储所有面板子类的字典 //单例 private static PanelManager instance; public static PanelManager Instance => instance; private void Awake() { if(instance == null) { instance = this; DontDestroyOnLoad(gameObject); } else{ Destroy(gameObject); } //初始化字典 panels = new Dictionary<string, BasePanel>(); panels.Clear(); canvasPos = GameObject.Find("Canvas").transform;//虽然有性能消耗但是只用一次所以可以接受 } /// <summary> /// 加载面板 /// </summary> /// <typeparam name="T">需要加载的子类面板脚本</typeparam> /// <param name="panelName">需要加载的面板名称</param> public void LoadPanel<T>(string panelName) where T:BasePanel,new () { T panel = new T();//实例化面板子类 if(canvasPos!=null) panel.CreatWnd(panelName, canvasPos); else Debug.Log("没有找到Canvas"); panels.Add(panelName, panel); panel.Init();//子类重写的抽象方法,用于执行自己的逻辑 } public void OpenPanel(string panelName) { if (panels.TryGetValue(panelName, out BasePanel curPanel)){ curPanel.OpenWnd(); } else Debug.Log($"没有找到正确的面板****{panelName}****,请检查预制体路径或者查看是否有该预制体"); } public void ClosePanel(string panelName) { if (panels.TryGetValue(panelName, out BasePanel panel)) { panel.CloseWnd(); } else Debug.Log($"没有找到正确的面板****{panelName}****,请检查预制体路径或者查看是否有该预制体"); } }
面板基类
cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class BasePanel {
protected Transform TempTrans { //记录要打开窗口的位置
get;
private set;
}
//实例化窗口
public virtual void CreatWnd(string wndName,Transform canvas)
{
//加载
GameObject wnd = Resources.Load<GameObject>("Wnd/"+wndName);
if( wnd!=null){
GameObject temp =GameObject.Instantiate(wnd);
temp.transform.SetParent(canvas,false);
//将该窗口的位置记录下来,以便打开和关闭
TempTrans = temp.transform;
CloseWnd(); //首次实例化不要直接打开
}
else{
Debug.Log("没有正确加载到面板");
}
}
//打开窗口
public virtual void OpenWnd()
{
TempTrans.gameObject.SetActive(true);
}
//关闭窗口
public virtual void CloseWnd() {
TempTrans.gameObject.SetActive(false);
}
public abstract void Init();
}
面板子类示例
cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TestPanel : BasePanel {
//在这里可以写TestWnd自己的逻辑
public override void Init() {
Debug.Log("子类可以在此进行自己的逻辑撰写");
}
}
使用示例
cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Ctrl : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
PanelManager.Instance.LoadPanel<TestPanel>("AnyWnd");
}
// Update is called once per frame
void Update()
{
if(Input.GetKeyDown(KeyCode.O))
{
PanelManager.Instance.OpenPanel("AnyWnd");
}
if (Input.GetKeyDown(KeyCode.P)) {
PanelManager.Instance.ClosePanel("AnyWnd");
}
}
}