10.UI模块
10.1 知识点
UI模块的作用
- 统一管理UI面板的显示相关
示意图
面板基类
导入必要的Unity引用
使用了一些Unity的命名空间,如UnityEngine、UnityEngine.Events、UnityEngine.EventSystems、UnityEngine.UI,以便使用Unity的功能和类。
定义BaseUIPanel类
创建了一个名为BaseUIPanel的抽象类,该类继承自MonoBehaviour,表示所有UI面板都应该继承自这个基类。
声明成员变量
- canvasGroup:用于控制UI面板的淡入淡出效果的CanvasGroup组件。
- alphaSpeed:淡入淡出的速度。
- isShow:标记UI面板是否正在显示。
- hideCallBack:当UI面板淡出成功时要执行的委托函数。
- UIElementDictionary:一个字典,用于存储所有的UI控件,以控件名字符串为键,控件列表为值。
在Awake方法中初始化UI控件
- 获取或添加CanvasGroup组件,用于控制UI面板的透明度。
- 调用FindPanelUIElement方法,初始化找到Panel下所有的UI控件,包括Button、Image、Text、Toggle、Slider、ScrollRect和InputField。
实现FindPanelUIElement方法
- 通过泛型参数T,获取子对象中所有当前类型的控件脚本。
- 遍历控件数组,根据控件名字将控件存储到UIElementDictionary中。
- 如果是Button控件,为其添加点击事件监听器。
- 如果是Toggle控件,为其添加值变化事件监听器。
实现OnButtonInitClick和OnToggleInitValueChanged方法
这些方法是虚方法,子类可以重写它们,根据控件名字执行不同的逻辑。
实现GetUIElement方法
通过控件名字从UIElementDictionary中获取对应类型的UI控件脚本。
实现AddUIElement和RemoveUIElement方法
用于动态添加和移除UI控件到/从UIElementDictionary中。
在Start方法中调用Init方法
Init方法是一个抽象方法,子类必须实现,用于初始化UI面板,包括按钮事件监听等内容。
实现ShowMe和HideMe方法
- ShowMe方法用于显示UI面板,将isShow标记设置为true,并将canvasGroup的透明度设置为0,实现淡入效果。
- HideMe方法用于隐藏UI面板,接受一个回调函数作为参数,将isShow标记设置为false,并将canvasGroup的透明度设置为1,实现淡出效果,并记录传入的淡出成功后会执行的函数。
在Update方法中处理淡入淡出效果
- 如果isShow为true且canvasGroup的透明度不等于1,则逐渐增加透明度以实现淡入效果。
- 如果isShow为false且canvasGroup的透明度不等于0,则逐渐减小透明度以实现淡出效果,当透明度接近0时,执行淡出回调函数。
面板管理器
导入必要的Unity引用
使用了一些Unity的命名空间,如UnityEngine、UnityEngine.Events、UnityEngine.EventSystems,以便使用Unity的功能和类。
定义BaseUIManager类
创建了一个名为BaseUIManager的类,继承自自定义的BaseSingletonInCSharp基类,表示UI管理器类。
声明成员变量
- panelDictionary:一个字典,用于存储面板的容器,以面板名字为键,面板脚本为值。
- UICanvasTransform:用于存储Canvas对象的引用,用于将UI面板显示在Canvas上。
在构造函数中获取Canvas对象
在构造函数中尝试通过GameObject.Find方法找到名为”UICanvas”的Canvas对象引用。如果找不到Canvas对象,就通过GameObject.Instantiate方法创建一个Canvas对象,并将其设置为DontDestroyOnLoad,以确保在场景切换时不被销毁。
实现ShowPanel方法
这个方法用于显示UI面板,接受一个泛型参数T,表示要显示的UI面板类型。
- 首先根据面板类型获取面板名字。
- 如果panelDictionary中已包含该面板名字的键,说明面板已经存在,直接返回该面板。
- 如果面板不存在,则加载对应的面板预制体,将其设置为Canvas的子对象,并获取面板脚本。
- 如果成功获取面板脚本,将面板添加到panelDictionary中,并调用其ShowMe方法来显示面板。
实现HidePanel方法
这个方法用于隐藏UI面板,接受一个泛型参数T,表示要隐藏的UI面板类型,以及一个isFade参数,用于控制是否淡出效果。
- 根据面板类型获取面板名字。
- 如果panelDictionary中包含该面板名字的键,说明面板存在。
- 如果isFade为true,则调用面板的HideMe方法,在面板淡出成功后销毁面板对象,并从panelDictionary中移除对应的键值对。
- 如果isFade为false,直接销毁面板对象,并从panelDictionary中移除对应的键值对。
实现GetPanel方法
这个方法用于获取UI面板,接受一个泛型参数T,表示要获取的UI面板类型。
- 根据面板类型获取面板名字。
- 如果panelDictionary中包含该面板名字的键,返回对应的面板脚本。
- 如果面板不存在,发出警告并返回null。
实现AddCustomEventListener方法
这个方法用于给UI控件添加自定义事件监听,接受控件对象、事件类型和响应函数作为参数。
- 首先获取控件上的EventTrigger组件,如果不存在,则添加一个EventTrigger组件。
- 创建一个EventTrigger.Entry对象,设置其事件类型为传入的eventTriggerType,并添加响应函数。
- 将该Entry对象添加到EventTrigger的事件列表中。
面板管理器对象
- 包括一个屏幕框架的Canvas和UI摄像机
进行测试
- 创建面板预制体,挂载面板脚本。面板脚本继承BaseUIPanel,关联面板控件并添加监听。
- 在适当的时候调用面板管理器切换面板
BaseUIManager.Instance.ShowPanel<TestPanel2>();
BaseUIManager.Instance.HidePanel<TestPanel1>();
注意:现在UI默认都是拿Resources下UI文件夹中的同名预制体,可以按照需要进行优化。也可以传进来路径去Resources下找。也可以在定义Canvas定义不同层级子物体,指定加入到什么层级。随着版本更新,UI逻辑可以进一步优化。
10.2 知识点代码
BaseUGUIPanel
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
// 基础UI面板类,所有UI面板应该继承自此类
public abstract class BaseUGUIPanel : MonoBehaviour
{
private CanvasGroup canvasGroup; // 用于控制UI面板的淡入淡出效果的CanvasGroup组件
private float alphaSpeed = 10; // 淡入淡出的速度
private bool isShow; // 标记UI面板是否正在显示
private UnityAction hideCallBack; // 当UI面板淡出成功时要执行的委托函数
//通过里式转换原则 来存储所有的控件 是通过控件名字符串绑定框架的 因为可能有同名的控件 放在同一个List里
private Dictionary<string, List<UIBehaviour>> UIElementDictionary = new Dictionary<string, List<UIBehaviour>>();
// Awake方法在对象实例化后立即调用
protected virtual void Awake()
{
// 获取或添加CanvasGroup组件
canvasGroup = GetComponent<CanvasGroup>();
if (canvasGroup == null)
{
canvasGroup = gameObject.AddComponent<CanvasGroup>();
if (canvasGroup == null)
{
Debug.LogError("Failed to add CanvasGroup component.");
}
}
//初始化找Panel下所有控件
FindPanelUIElement<Button>();
FindPanelUIElement<Image>();
FindPanelUIElement<Text>();
FindPanelUIElement<Toggle>();
FindPanelUIElement<Slider>();
FindPanelUIElement<ScrollRect>();
FindPanelUIElement<InputField>();
}
/// <summary>
/// 找到子对象的对应控件
/// </summary>
/// <typeparam name="T"></typeparam>
private void FindPanelUIElement<T>() where T : UIBehaviour
{
//获得子对象中所有当前类型的控件脚本
T[] controls = this.GetComponentsInChildren<T>();
//
for (int i = 0; i < controls.Length; ++i)
{
string objName = controls[i].gameObject.name;
if (UIElementDictionary.ContainsKey(objName))
UIElementDictionary[objName].Add(controls[i]);
else
UIElementDictionary.Add(objName, new List<UIBehaviour>() { controls[i] });
//如果是按钮控件
if (controls[i] is Button)
{
(controls[i] as Button).onClick.AddListener(() =>
{
OnButtonInitClick(objName);
});
}
//如果是单选框或者多选框
else if (controls[i] is Toggle)
{
(controls[i] as Toggle).onValueChanged.AddListener((value) =>
{
OnToggleInitValueChanged(objName, value);
});
}
}
}
//按钮初始化时的监听函数 重写后 分支判断名字执行不同逻辑
protected virtual void OnButtonInitClick(string btnName)
{
}
//多选框初始化时的监听函数 重写后 分支判断名字执行不同逻辑
protected virtual void OnToggleInitValueChanged(string toggleName, bool value)
{
}
/// <summary>
/// 得到对应名字的对应控件脚本
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="uIElement"></param>
/// <returns></returns>
protected T GetUIElement<T>(string uIElement) where T : UIBehaviour
{
if (UIElementDictionary.ContainsKey(uIElement))
{
for (int i = 0; i < UIElementDictionary[uIElement].Count; ++i)
{
if (UIElementDictionary[uIElement][i] is T)
return UIElementDictionary[uIElement][i] as T;
}
}
return null;
}
// 动态添加控件
protected void AddUIElement<T>(string name, T uiElement) where T : UIBehaviour
{
if (UIElementDictionary.ContainsKey(name))
{
UIElementDictionary[name].Add(uiElement);
}
else
{
UIElementDictionary.Add(name, new List<UIBehaviour>() { uiElement });
}
}
// 动态移除控件
protected void RemoveUIElement<T>(string name, T uiElement) where T : UIBehaviour
{
if (UIElementDictionary.ContainsKey(name))
{
UIElementDictionary[name].Remove(uiElement);
}
}
// Start方法在第一帧之前调用
protected virtual void Start()
{
Init(); // 调用子类的Init方法,用于初始化按钮事件监听等内容
}
// 子类必须实现的抽象方法,用于初始化UI面板
public abstract void Init();
// 显示UI面板时调用的方法
public virtual void ShowMe()
{
isShow = true;
canvasGroup.alpha = 0; // 将透明度设置为0,实现淡入效果
}
// 隐藏UI面板时调用的方法,接受一个回调函数作为参数
public virtual void HideMe(UnityAction callBack)
{
isShow = false;
canvasGroup.alpha = 1; // 将透明度设置为1,实现淡出效果
hideCallBack = callBack; // 记录传入的淡出成功后会执行的函数
}
// Update方法在每一帧调用
void Update()
{
// 淡入
if (isShow && !Mathf.Approximately(canvasGroup.alpha, 1))
{
canvasGroup.alpha = Mathf.MoveTowards(canvasGroup.alpha, 1, alphaSpeed * Time.deltaTime);
}
// 淡出
else if (!isShow && !Mathf.Approximately(canvasGroup.alpha, 0))
{
canvasGroup.alpha = Mathf.MoveTowards(canvasGroup.alpha, 0, alphaSpeed * Time.deltaTime);
if (Mathf.Approximately(canvasGroup.alpha, 0))
{
hideCallBack?.Invoke(); // 在淡出完成后执行回调函数
}
}
}
}
BaseUGUIManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
// 基础UI管理器类,用于管理UI面板的创建、显示和隐藏
public class BaseUGUIManager : BaseSingletonInCSharp<BaseUGUIManager>
{
private Dictionary<string, BaseUGUIPanel> panelDictionary = new Dictionary<string, BaseUGUIPanel>(); // 存储面板的容器
private Transform UICanvasTransform; // Canvas对象的引用
public BaseUGUIManager()
{
// 在构造函数中获取Canvas对象
try
{
UICanvasTransform = GameObject.Find("UICanvas").transform;
Debug.Log("UICanvas have found.");
}
catch
{
if (UICanvasTransform == null)
{
UICanvasTransform = GameObject.Instantiate(Resources.Load<GameObject>("UI/UICanvas")).transform;
Debug.Log("UICanvas Instantiate.");
}
}
if (UICanvasTransform == null)
{
Debug.LogError("UICanvas not found.");
}
else
{
GameObject.DontDestroyOnLoad(UICanvasTransform.gameObject);
}
}
// 显示面板
public T ShowPanel<T>() where T : BaseUGUIPanel
{
string panelName = typeof(T).Name;
if (panelDictionary.ContainsKey(panelName))
{
return panelDictionary[panelName] as T;
}
// 加载面板预制体
GameObject panelObj = GameObject.Instantiate(Resources.Load<GameObject>("UI/" + panelName));
panelObj.transform.SetParent(UICanvasTransform, false);
// 获取面板脚本
T panel = panelObj.GetComponent<T>();
if (panel != null)
{
panelDictionary.Add(panelName, panel);
panel.ShowMe();
}
else
{
Debug.LogError("Failed to get panel script.");
}
return panel;
}
// 隐藏面板
public void HidePanel<T>(bool isFade = true) where T : BaseUGUIPanel
{
string panelName = typeof(T).Name;
if (panelDictionary.ContainsKey(panelName))
{
if (isFade)
{
panelDictionary[panelName].HideMe(() =>
{
// 面板淡出成功后销毁面板
GameObject.Destroy(panelDictionary[panelName].gameObject);
panelDictionary.Remove(panelName);
});
}
else
{
// 直接销毁面板
GameObject.Destroy(panelDictionary[panelName].gameObject);
panelDictionary.Remove(panelName);
}
}
else
{
Debug.LogWarning("Panel not found: " + panelName);
}
}
// 获取面板
public T GetPanel<T>() where T : BaseUGUIPanel
{
string panelName = typeof(T).Name;
if (panelDictionary.ContainsKey(panelName))
{
return panelDictionary[panelName] as T;
}
else
{
Debug.LogWarning("Panel not found: " + panelName);
return null;
}
}
/// <summary>
/// 给控件添加自定义事件监听
/// </summary>
/// <param name="uIElement">控件对象</param>
/// <param name="eventTriggerType">事件类型</param>
/// <param name="callBack">事件的响应函数</param>
public static void AddCustomEventListener(UIBehaviour uIElement, EventTriggerType eventTriggerType, UnityAction<BaseEventData> callBack)
{
EventTrigger trigger = uIElement.GetComponent<EventTrigger>();
if (trigger == null)
trigger = uIElement.gameObject.AddComponent<EventTrigger>();
EventTrigger.Entry entry = new EventTrigger.Entry();
entry.eventID = eventTriggerType;
entry.callback.AddListener(callBack);
trigger.triggers.Add(entry);
}
}
TestPanel1
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class TestPanel1 : BaseUGUIPanel
{
//public Button btnChangeTestPanel2;
public override void Init()
{
//btnChangeTestPanel2.onClick.AddListener(() =>
//{
// BaseUGUIManager.Instance.ShowPanel<TestPanel2>();
// BaseUGUIManager.Instance.HidePanel<TestPanel1>();
//});
//GetUIElement<Button>("btnChangeTestPanel2");
}
protected override void OnButtonInitClick(string btnName)
{
base.OnButtonInitClick(btnName);
switch(btnName)
{
case "btnChangeTestPanel2":
BaseUGUIManager.Instance.ShowPanel<TestPanel2>();
BaseUGUIManager.Instance.HidePanel<TestPanel1>();
break;
}
}
}
TestPanel2
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class TestPanel2 : BaseUGUIPanel
{
public Button btnChangeTestPanel1;
public override void Init()
{
this.GetUIElement<Button>("btnChangeTestPanel1").onClick.AddListener((() =>
{
BaseUGUIManager.Instance.ShowPanel<TestPanel1>();
BaseUGUIManager.Instance.HidePanel<TestPanel2>();
}));
// btnChangeTestPanel1.onClick.AddListener(() =>
// {
// BaseUGUIManager.Instance.ShowPanel<TestPanel1>();
// BaseUGUIManager.Instance.HidePanel<TestPanel2>();
// });
}
}
Lesson10_UI模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson10_UI模块 : MonoBehaviour
{
void Start()
{
BaseUGUIManager.Instance.ShowPanel<TestPanel1>();
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com