目的
当游戏开发者再创建UI面板的时候一般是有一套流程的
而为了简化这个流程演化出UI框架
1、制作UI面板基类
帮助我们自动化的查找组件,监听事件,无需每次写大量冗余代码
2、制作UI管理器
管理所有UI面板,UI面板的显示隐藏都通过UI管理器来进行管理
基础版代码:
UI基类:
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public abstract class BasePanel : MonoBehaviour
{
/// <summary>
/// 用于存储所有要用到的UI控件,用历史替换原则 父类装子类
/// </summary>
protected Dictionary<string, UIBehaviour> controlDic = new Dictionary<string, UIBehaviour>();
/// <summary>
/// 控件默认名字 如果得到的控件名字存在于这个容器 意味着我们不会通过代码去使用它 它只会是起到显示作用的控件
/// </summary>
private static List<string> defaultNameList = new List<string>() { "Image",
"Text (TMP)",
"RawImage",
"Background",
"Checkmark",
"Label",
"Text (Legacy)",
"Arrow",
"Placeholder",
"Fill",
"Handle",
"Viewport",
"Scrollbar Horizontal",
"Scrollbar Vertical"};
protected virtual void Awake()
{
//为了避免 某一个对象上存在两种控件的情况
//我们应该优先查找重要的组件
FindChildrenControl<Button>();
FindChildrenControl<Toggle>();
FindChildrenControl<Slider>();
FindChildrenControl<InputField>();
FindChildrenControl<ScrollRect>();
FindChildrenControl<Dropdown>();
//即使对象上挂在了多个组件 只要优先找到了重要组件
//之后也可以通过重要组件得到身上其他挂载的内容
FindChildrenControl<Text>();
FindChildrenControl<TextMeshPro>();
FindChildrenControl<Image>();
}
/// <summary>
/// 面板显示时会调用的逻辑
/// </summary>
public abstract void ShowMe();
/// <summary>
/// 面板隐藏时会调用的逻辑
/// </summary>
public abstract void HideMe();
/// <summary>
/// 获取指定名字以及指定类型的组件
/// </summary>
/// <typeparam name="T">组件类型</typeparam>
/// <param name="name">组件名字</param>
/// <returns></returns>
public T GetControl<T>(string name) where T:UIBehaviour
{
if(controlDic.ContainsKey(name))
{
T control = controlDic[name] as T;
if (control == null)
Debug.LogError($"不存在对应名字{name}类型为{typeof(T)}的组件");
return control;
}
else
{
Debug.LogError($"不存在对应名字{name}的组件");
return null;
}
}
protected virtual void ClickBtn(string btnName)
{
}
protected virtual void SliderValueChange(string sliderName, float value)
{
}
protected virtual void ToggleValueChange(string sliderName, bool value)
{
}
private void FindChildrenControl<T>() where T:UIBehaviour
{
T[] controls = this.GetComponentsInChildren<T>(true);
for (int i = 0; i < controls.Length; i++)
{
//获取当前控件的名字
string controlName = controls[i].gameObject.name;
//通过这种方式 将对应组件记录到字典中
if (!controlDic.ContainsKey(controlName))
{
if(!defaultNameList.Contains(controlName))
{
controlDic.Add(controlName, controls[i]);
//判断控件的类型 决定是否加事件监听
if(controls[i] is Button)
{
(controls[i] as Button).onClick.AddListener(() =>
{
ClickBtn(controlName);
});
}
else if(controls[i] is Slider)
{
(controls[i] as Slider).onValueChanged.AddListener((value) =>
{
SliderValueChange(controlName, value);
});
}
else if(controls[i] is Toggle)
{
(controls[i] as Toggle).onValueChanged.AddListener((value) =>
{
ToggleValueChange(controlName, value);
});
}
}
}
}
}
}
UI管理器:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
/// <summary>
/// 层级枚举
/// </summary>
public enum E_UILayer
{
/// <summary>
/// 最底层
/// </summary>
Bottom,
/// <summary>
/// 中层
/// </summary>
Middle,
/// <summary>
/// 高层
/// </summary>
Top,
/// <summary>
/// 系统层 最高层
/// </summary>
System,
}
/// <summary>
/// 管理所有UI面板的管理器
/// 注意:面板预设体名要和面板类名一致!!!!!
/// </summary>
public class UIMgr : BaseManager<UIMgr>
{
private Camera uiCamera;
private Canvas uiCanvas;
private EventSystem uiEventSystem;
//层级父对象
private Transform bottomLayer;
private Transform middleLayer;
private Transform topLayer;
private Transform systemLayer;
/// <summary>
/// 用于存储所有的面板对象
/// </summary>
private Dictionary<string, BasePanel> panelDic = new Dictionary<string, BasePanel>();
private UIMgr()
{
//动态创建唯一的Canvas和EventSystem(摄像机)
uiCamera = GameObject.Instantiate(ResMgr.Instance.Load<GameObject>("UI/UICamera")).GetComponent<Camera>();
//ui摄像机过场景不移除 专门用来渲染UI面板
GameObject.DontDestroyOnLoad(uiCamera.gameObject);
//动态创建Canvas
uiCanvas = GameObject.Instantiate(ResMgr.Instance.Load<GameObject>("UI/Canvas")).GetComponent<Canvas>();
//设置使用的UI摄像机
uiCanvas.worldCamera = uiCamera;
//过场景不移除
GameObject.DontDestroyOnLoad(uiCanvas.gameObject);
//找到层级父对象
bottomLayer = uiCanvas.transform.Find("Bottom");
middleLayer = uiCanvas.transform.Find("Middle");
topLayer = uiCanvas.transform.Find("Top");
systemLayer = uiCanvas.transform.Find("System");
//动态创建EventSystem
uiEventSystem = GameObject.Instantiate(ResMgr.Instance.Load<GameObject>("UI/EventSystem")).GetComponent<EventSystem>();
GameObject.DontDestroyOnLoad(uiEventSystem.gameObject);
}
/// <summary>
/// 获取对应层级的父对象
/// </summary>
/// <param name="layer">层级枚举值</param>
/// <returns></returns>
public Transform GetLayerFather(E_UILayer layer)
{
switch (layer)
{
case E_UILayer.Bottom:
return bottomLayer;
case E_UILayer.Middle:
return middleLayer;
case E_UILayer.Top:
return topLayer;
case E_UILayer.System:
return systemLayer;
default:
return null;
}
}
/// <summary>
/// 显示面板
/// </summary>
/// <typeparam name="T">面板的类型</typeparam>
/// <param name="layer">面板显示的层级</param>
/// <param name="callBack">由于可能是异步加载 因此通过委托回调的形式 将加载完成的面板传递出去进行使用</param>
/// <param name="isSync">是否采用同步加载 默认为false</param>
public void ShowPanel<T>(E_UILayer layer = E_UILayer.Middle, UnityAction<T> callBack = null, bool isSync = false) where T:BasePanel
{
//获取面板名 预设体名必须和面板类名一致
string panelName = typeof(T).Name;
//存在面板
if(panelDic.ContainsKey(panelName))
{
//如果要显示面板 会执行一次面板的默认显示逻辑
panelDic[panelName].ShowMe();
//如果存在回调 直接返回出去即可
callBack?.Invoke(panelDic[panelName] as T);
return;
}
//不存在面板 加载面板
ABResMgr.Instance.LoadResAsync<GameObject>("UI", panelName, (res) =>
{
//层级的处理
Transform father = GetLayerFather(layer);
//避免没有按指定规则传递层级参数 避免为空
if (father == null)
father = middleLayer;
//将面板预设体创建到对应父对象下 并且保持原本的缩放大小
GameObject panelObj = GameObject.Instantiate(res, father, false);
//获取对应UI组件返回出去
T panel = panelObj.GetComponent<T>();
//显示面板时执行的默认方法
panel.ShowMe();
//传出去使用
callBack?.Invoke(panel);
//存储panel
panelDic.Add(panelName, panel);
}, isSync);
}
/// <summary>
/// 隐藏面板
/// </summary>
/// <typeparam name="T">面板类型</typeparam>
public void HidePanel<T>() where T : BasePanel
{
string panelName = typeof(T).Name;
if(panelDic.ContainsKey(panelName))
{
//执行默认的隐藏面板想要做的事情
panelDic[panelName].HideMe();
//销毁面板
GameObject.Destroy(panelDic[panelName].gameObject);
//从容器中移除
panelDic.Remove(panelName);
}
}
/// <summary>
/// 获取面板
/// </summary>
/// <typeparam name="T">面板的类型</typeparam>
public T GetPanel<T>() where T:BasePanel
{
string panelName = typeof(T).Name;
if (panelDic.ContainsKey(panelName))
return panelDic[panelName] as T;
return null;
}
}
只提供基础版