Unity | Unity中UI框架的实现与使用
Posted catalpa_ovis
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity | Unity中UI框架的实现与使用相关的知识,希望对你有一定的参考价值。
0 前言
作为初学者常用的UI设计方式以及与UI与游戏对象交互的方式是使用简单的对象拖拽,但是这样的方式耦合性很高,而且对于协作项目来说不太友好,所以使用一个单一的UI框架管理Resources中的UIPanel预制体,在需要时进行创建和销毁是很有必要的,其一可以降低UI与游戏程序之间的耦合性,提高UI的复用能力,其二可以优化游戏性能,在必要的时候才创建所需的UI组件,减少在开始时大量创建UI的性能开销。
1 程序结构
UIManager
- UIManager.cs : Class
- BasePanel.cs : Class
- UIPanelInfo.cs : Class
- UIPanelType.cs : Enum
- UIPanel.json : Json
2 工具类:UIPanelInfo.cs
该类用于对Json文件进行反序列化,将Json文本中的路径和类型字段转换为对应的对象,利用了C#中对Json进行序列化操作的原生接口ISerializationCallbackReceiver的回调方法处理了Json对象。
using System;
using UnityEngine;
// 反序列化json文本,读取其中存储的Panel路径
[Serializable]
public class UIPanelInfo : ISerializationCallbackReceiver
[NonSerialized]
public UIPanelType panelType;
public string panelTypeString;
public string path;
//序列化
public void OnBeforeSerialize()
// 反序列化,从文本到对象
public void OnAfterDeserialize()
var type = (UIPanelType)System.Enum.Parse(typeof(UIPanelType), panelTypeString);
panelType = type;
3 PanelType枚举类以及BasePanel类
PanelType类用于为单个Panel赋予唯一Key用来生成和调用Panel。
public enum UIPanelType
PanelType,
...
BasePanel类是对所有Panel的声明周期进行了定义的基类,在其中我对GetOverUI和FindUIElem方法进行了封装,用来获得所需要的对象,其余方法是对Panel生命周期的虚函数声明,包括OnEnter(进入时)、OnPause(暂停时)、OnResume(继续时)、OnExit(退出时)、OnClose(关闭时)这五个生命周期,在继承其的基类中,可以override这几个虚函数,用来对不同生命周期中的Panel进行操作,同时强推一下CanvasGroup这个组件,对于Panel的效果实现有很大帮助。
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class BasePanel : MonoBehaviour
private CanvasGroup canvasGroup;
public CanvasGroup CanvasGroupValue
get
return canvasGroup;
set
canvasGroup = value;
private void Awake()
canvasGroup = GetComponent<CanvasGroup>();
/// <summary>
/// 显示UI时调用
/// </summary>
public virtual void OnEnter()
canvasGroup.alpha = 1;
public virtual void OnPause()
canvasGroup.blocksRaycasts = false;
public virtual void OnResume()
canvasGroup.blocksRaycasts = true;
public virtual void OnExit()
canvasGroup.alpha = 0;
public virtual void OnClose()
UIManager.Instance.PopPanel();
protected GameObject GetOverUI(GameObject canvas)
PointerEventData pointerEventData = new PointerEventData(EventSystem.current);
pointerEventData.position = Input.mousePosition;
GraphicRaycaster gr = canvas.GetComponent<GraphicRaycaster>();
List<RaycastResult> results = new List<RaycastResult>();
gr.Raycast(pointerEventData, results);
if (results.Count != 0)
return results[0].gameObject;
return null;
protected GameObject FindUIElement(string elementName)
GameObject returnGameObject = null;
foreach (var child in gameObject.GetComponentsInChildren<Transform>(true))
if (child.name.Equals(elementName))
returnGameObject = child.gameObject;
return returnGameObject;
4 UIManager类
UIManager类是该UI框架的主要逻辑类,这个类的数据结构由存放Json中path字段的字典、每个BasePanel组件的字典、以及一个存放了Panel对象的栈组成,同时因为UIManager的特性,在类设计上使用了单例模式。
构成该类的方法主要是对Panel栈的操作方法,包括PushPanel(入栈)和PopPanel(出栈)两个主要方法。
最后为该类设计了一个内部类用来获取Json中的path和panelType,这个类使用了JsonUtility来获得Json对象的信息。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityEngine.Video;
public class UIManager : MonoBehaviour
private static UIManager _instance;
public static UIManager Instance
get
if (_instance == null)
_instance = new UIManager();
return _instance;
private Transform _canvasTransform;
private Transform CanvasTransform
get
if (_canvasTransform == null)
_canvasTransform = GameObject.Find("MainCanvas").transform;
return _canvasTransform;
private Dictionary<UIPanelType, string> panelPathDictionary;
private Dictionary<UIPanelType, BasePanel> panelDictionary;
private Stack<BasePanel> panelStack;
private UIManager()
ParseUIPanelTypeJson();
public void PushPanel(UIPanelType panelType)
if (panelStack == null)
panelStack = new Stack<BasePanel>();
if (panelStack.Count > 0)
BasePanel topPanel = panelStack.Peek();
topPanel.OnPause();
BasePanel panel = GetPanel(panelType);
panel.OnEnter();
panelStack.Push(panel);
public void PopPanel()
if (panelStack == null)
panelStack = new Stack<BasePanel>();
if (panelStack.Count <= 0)
return;
BasePanel basePanel = panelStack.Pop();
basePanel.OnExit();
if (panelStack.Count <= 0) return;
BasePanel topPanel = panelStack.Peek();
topPanel.OnResume();
private BasePanel GetPanel(UIPanelType panelType)
if (panelDictionary == null)
panelDictionary = new Dictionary<UIPanelType, BasePanel>();
panelDictionary.TryGetValue(panelType, out var panel);
if (panel == null)
panelPathDictionary.TryGetValue(panelType, out var path);
GameObject initPanel = Instantiate(Resources.Load(path)) as GameObject;
initPanel.transform.SetParent(CanvasTransform, false);
panelDictionary.Add(panelType, initPanel.GetComponent<BasePanel>());
return initPanel.GetComponent<BasePanel>();
else
return panel;
[SerializeField]
class UIPanelTypeJson
public List<UIPanelInfo> infoList;
private void ParseUIPanelTypeJson()
panelPathDictionary = new Dictionary<UIPanelType, string>();
TextAsset textAsset = Resources.Load<TextAsset>("Json/UIPanels");
UIPanelTypeJson jsObject = JsonUtility.FromJson<UIPanelTypeJson>(textAsset.text);
foreach (UIPanelInfo info in jsObject.infoList)
panelPathDictionary.Add(info.panelType, info.path);
5 UIPanelJson文件
用来存放Panel预制体路径和对应的panelType
"infoList": [
"panelTypeString" : "MainMenuPanel",
"path" : "UIPanel/MainMenuPanel"
]
5 使用方法
- 在json文件中写入对应的Panel预制体路径和panelType
"infoList": [
"panelTypeString" : "PanelType",
"path" : "UIPanel/XXXPanel"
]
- 在PanelType枚举类中写入Json文件中创建的PanelType
public enum UIPanelType
PanelType
- 在方法中调用PushPanel方法创建panelTyep对应的Panel对象
UIManager.Instance.PushPanel(panelType);
- 修改XXXPanel类中的生命周期方法来实现面板效果(示例:一个简单的UI界面类)
using System;
using System.Collections;
using DG.Tweening;
using UnityEngine;
using UnityEngine.UI;
public class UserInterfacePanel : BasePanel
private int Health;
private int sub = 0;
private int ableBullet = 0;
void Awake()
EventCenter.AddListener<int>(EventType.BulletRefresh, RefreshBullet);
EventCenter.AddListener<int>(EventType.HealthRefresh, RefreshHealth);
private void Start()
StartCoroutine(CheckTheNumericValue());
void RefreshHealth(int health)
var text = transform.Find("Health").transform.Find("OnTimeHealth").GetComponent<Text>();
text.text = health.ToString();
void RefreshBullet(int bulletContain)
ableBullet = bulletContain;
public override void OnEnter()
UIManager.Instance.PushPanel(UIPanelType.TipPanel);
UIManager.Instance.PushPanel(UIPanelType.PackagePanel);
IEnumerator CheckTheNumericValue()
while (true)
yield return new WaitForSeconds(0.1f);
var text = FindUIElement("BulletContain").GetComponent<Text>();
if (Package.Instance.GetItem(ItemType._9mmAmmo) != null)
text.text = $"ableBullet / Package.Instance.GetItem(ItemType._9mmAmmo).count";
6 结篇
框架对程序结构以及性能的优化是十分强大的,可以多用。
以上是关于Unity | Unity中UI框架的实现与使用的主要内容,如果未能解决你的问题,请参考以下文章