Unity UI框架
一、简介
最近在各大网站看了一下 Unity3d 的 UI 框架,各种 UI 框架已经有很多的版本了,各有千秋,有的功能虽然写的完善,但用起来太复杂,有的框架功能不完善,搞个课程就上架了,还有什么 MVC 框架,绕来绕去的看的头都大了,这些根本不想用。
于是我自己就写了一个 UI 框架,只有两个脚本,不用向 UI 预制体上挂载脚本,所有的组件访问都可以通过 UIManager 来控制,常用的几个方法:显示界面,关闭界面,查找子物体,就这么多。
二、UI 框架
下面的两个脚本是 UI 框架的核心部分,具体的用法在下面的章节有介绍
UIBase
using UnityEngine;public class UIBase
{#region 字段/// <summary>/// Prefabs路径/// </summary>public string PrefabsPath { get; set; }/// <summary>/// UI面板的名字/// </summary>public string UIName { get; set; }/// <summary>/// 当前UI所在的场景名/// </summary>public string SceneName { get; set; }/// <summary>/// Type 的全名/// </summary>public string FullName { get; set; }/// <summary>/// 当前UI的游戏物体/// </summary>public GameObject UIGameObject { get; set; }#endregion/// <summary>/// 面板实例化时执行一次/// </summary>public virtual void Start() { }/// <summary>/// 每帧执行/// </summary>public virtual void Update() { }/// <summary>/// 当前UI面板销毁之前执行一次/// </summary>public virtual void Destroy() { }/// <summary>/// 根据名称查找一个子对象/// </summary>/// <param name="name"></param>/// <returns></returns>public GameObject GetObject(string name){Transform[] trans = UIGameObject.GetComponentsInChildren<Transform>();foreach (var item in trans){if (item.name == name)return item.gameObject;}Debug.LogError(string.Format("找不到名为 {0} 的子对象", name));return null;}/// <summary>/// 根据名称获取一个子对象的组件/// </summary>/// <typeparam name="T"></typeparam>/// <param name="name"></param>/// <returns></returns>public T GetOrAddCommonent<T>(string name) where T : Component{GameObject child = GetObject(name);if (child){if (child.GetComponent<T>() == null)child.AddComponent<T>();return child.GetComponent<T>();}return null;}protected UIBase() { }
}
UIManager
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;public class UIManager : MonoBehaviour
{public static UIManager Instance;//存储场景中的UI信息private Dictionary<string, UIBase> UIDic = new Dictionary<string, UIBase>();//当前场景的 Canvas 游戏物体private Transform CanvasTransform = null;//当前字典中UI的个数public int UICount{get { return UIDic.Count; }}private void Awake(){Instance = this;}private void Start(){}private void Update(){if (UIDic.Count > 0){foreach (var key in UIDic.Keys){if (UIDic[key] != null)UIDic[key].Update();}}}/// <summary>/// 显示面板/// </summary>/// <typeparam name="T"></typeparam>/// <returns></returns>public UIBase ShowUI<T>() where T : UIBase{Type t = typeof(T);string fullName = t.FullName;if (UIDic.ContainsKey(fullName)){Debug.Log("当前面板已经显示了,名字:" + fullName);return UIDic[fullName];}GameObject canvasObj = GameObject.Find("Canvas");if (canvasObj == null){Debug.LogError("场景中没有Canvas组件,无法显示UI物体");return null;}CanvasTransform = canvasObj.transform;UIBase uiBase = Activator.CreateInstance(t) as UIBase;if (string.IsNullOrEmpty(uiBase.PrefabsPath)){Debug.LogError("Prefabs 路径不能为空");return null;}GameObject prefabs = Resources.Load<GameObject>(uiBase.PrefabsPath);GameObject uiGameOjbect = GameObject.Instantiate(prefabs, CanvasTransform);uiGameOjbect.name = uiBase.PrefabsPath.Substring(uiBase.PrefabsPath.LastIndexOf('/') + 1);uiBase.UIName = uiGameOjbect.name;uiBase.SceneName = SceneManager.GetActiveScene().name;uiBase.UIGameObject = uiGameOjbect;uiBase.FullName = fullName;uiBase.Start();UIDic.Add(fullName, uiBase);return uiBase;}/// <summary>/// 移除面板/// </summary>/// <typeparam name="T"></typeparam>public void RemoveUI<T>(){Type t = typeof(T);string fullName = t.FullName;if (UIDic.ContainsKey(fullName)){UIBase uIBase = UIDic[fullName];uIBase.Destroy();GameObject.Destroy(uIBase.UIGameObject);UIDic.Remove(fullName);return;}Debug.Log(string.Format("当前的UI物体未实例化,名字:{0}", fullName));}/// <summary>/// 清除所有的UI物体/// </summary>public void ClearAllPanel(){foreach (var key in UIDic.Keys){UIBase uIBase = UIDic[key];if (uIBase != null){uIBase.Destroy();GameObject.Destroy(uIBase.UIGameObject);}}UIDic.Clear();}/// <summary>/// 找到指定的UI面板/// </summary>/// <typeparam name="T"></typeparam>/// <param name="name"></param>/// <returns></returns>public GameObject GetGameObject<T>(string name){Type t = typeof(T);string fullName = t.FullName;UIBase uIBase = null;if (!UIDic.TryGetValue(fullName, out uIBase)){Debug.Log("没有找到对应的UI面板,名字:" + fullName);return null;}return uIBase.GetObject(name);}private UIManager() { }
}
三、UI 框架的用法
我用了两个 场景来测试框架,start 和 main 场景
start 场景如下:
在 Start 场景 GameRoot 上挂上 StartSceneRoot 脚本
这个脚本就调用框架在场景中显示一个UI
using UnityEngine;public class StartSceneRoot : MonoBehaviour { void Start () {UIManager.Instance.ShowUI<Panel_MainUI>();}
}
在 GameManager 游戏物体上有两个脚本,这个是要跟随着场景一起跳转的。
UIManager 的代码在上一节,下面是 GameManager 代码
using UnityEngine;
using UnityEngine.SceneManagement;public class GameManager : MonoBehaviour {public static GameManager Instance;private static bool origional = true;private void Awake(){if (origional){Instance = this as GameManager;origional = false;DontDestroyOnLoad(this.gameObject);}else{Destroy(this.gameObject);}}void Start () {SceneManager.sceneUnloaded += SceneManager_sceneUnloaded;SceneManager.sceneLoaded += SceneManager_sceneLoaded;}private void SceneManager_sceneLoaded(Scene arg0, LoadSceneMode arg1){//Debug.Log("场景加载了,场景名:" + arg0.name);}private void SceneManager_sceneUnloaded(Scene arg0){//Debug.Log("场景卸载了,场景名:" + arg0.name);//注意:切换场景要清除掉 UIManager 中保存的 UI 数据UIManager.Instance.ClearAllPanel();}public void LoadScene(string sceneName){if (SceneManager.GetActiveScene().name != sceneName){SceneManager.LoadScene(sceneName);}}private GameManager() { }
}
在 start 场景中挂载到游戏物体上的脚本就这三个:StartSceneRoot,GameManager,UIManager
下面就准备要显示的 UI 了,在这里,我做了三个 UI 界面,并做成预制体
界面 Panel_MainUI
界面 Panel_Setting
界面 Panel_Affiche
这三个预制体对应的也是三个脚本,但不用挂在游戏物体上,作用是UI的逻辑部分。
Panel_MainUI
using UnityEngine;
using UnityEngine.UI;public class Panel_MainUI : UIBase
{public Panel_MainUI(){PrefabsPath = "Prefabs/UI/Panel_MainUI";}public override void Start(){GetOrAddCommonent<Button>("Button_Setting").onClick.AddListener(() =>{//显示设置面板UIManager.Instance.ShowUI<Panel_Setting>();});GetOrAddCommonent<Button>("Button_Task").onClick.AddListener(() =>{//清除所有的面板//UIManager.Instance.ClearAllPanel(); //跳转到 main 场景GameManager.Instance.LoadScene("main");});GetOrAddCommonent<Button>("Button_Equipage").onClick.AddListener(() =>{Debug.Log("UIManager 中 UI 面板的个数:" + UIManager.Instance.UICount);});}public override void Update(){//Debug.Log("我是 Panel_MainUI Update 方法");}public override void Destroy(){//Debug.Log("我是 Panel_MainUI Destroy 方法");}
}
Panel_Setting
using UnityEngine;
using UnityEngine.UI;public class Panel_Setting : UIBase
{public Panel_Setting(){PrefabsPath = "Prefabs/UI/Panel_Setting";}public override void Start(){//Debug.Log("我是 Panel_Setting Start 方法");GetOrAddCommonent<Button>("Button_Close").onClick.AddListener(() =>{//移除自己UIManager.Instance.RemoveUI<Panel_Setting>();});GetOrAddCommonent<Button>("Button_Test").onClick.AddListener(() =>{//访问其他的面板的游戏物体 GameObject obj = UIManager.Instance.GetGameObject<Panel_MainUI>("Button_Map");if (obj != null)obj.transform.Find("Text").GetComponent<Text>().text = "Map";});GetOrAddCommonent<Button>("Button_Test1").onClick.AddListener(() =>{Debug.Log("UIManager 中 UI 面板的个数:" + UIManager.Instance.UICount);});}public override void Update(){//Debug.Log("我是 Panel_Setting Update 方法");}public override void Destroy(){//Debug.Log("我是 Panel_Setting Destroy 方法");}
}
Panel_Affiche
using UnityEngine;
using UnityEngine.UI;public class Panel_Affiche : UIBase
{public Panel_Affiche(){PrefabsPath = "Prefabs/UI/Panel_Affiche";}public override void Start(){GetOrAddCommonent<Button>("Button_Close").onClick.AddListener(() =>{GameManager.Instance.LoadScene("start");});}public override void Update(){//Debug.Log("我是 Panel_Affiche Update 方法");}public override void Destroy(){//Debug.Log("我是 Panel_Affiche Destroy 方法");}
}
main 场景只挂了一个脚本
MainSceneRoot 脚本同样也是只是用来显示UI用的
using UnityEngine;public class MainSceneRoot : MonoBehaviour {void Start () {UIManager.Instance.ShowUI<Panel_Affiche>();}
}
运行后就能看到,显示了 Panel_MainUI 的UI界面,点击设置,就会打开设置的界面,点击任务按钮,就会跳转到 main 场景(这里只是测试)
跳转到 main 场景后,点击关闭按钮,又会返回到 start 场景。
演示就这些了,写这个框架我也就用了几个小时而已,当然还有待继续完善和改进的,另外,我用的 Unity版本是 Unity 5.6.7f1 ,C# 的一些高级语法用不了,不然可以写的更优雅一些。
源码:点击跳转
结束
如果这个帖子对你有所帮助,欢迎 关注 + 点赞 + 留言
end
相关文章:

Unity UI框架
一、简介 最近在各大网站看了一下 Unity3d 的 UI 框架,各种 UI 框架已经有很多的版本了,各有千秋,有的功能虽然写的完善,但用起来太复杂,有的框架功能不完善,搞个课程就上架了,还有什么 MVC 框…...

vue2提取vue-router的title单独存放,使用i18n实现
成品效果 首先引入i18n(vue-i18n官网文档) 依赖包 npm install vue-i18n8然后单独在src目录下新建一个文件夹lang,存放相对应的变量名称,我这里只做显示中文所以其他引入我都注释了,具体目录如下: src\lang/zh.js部分代码 export…...
【Linux操作系统】【综合实验三 用户帐号、文件系统与系统安全管理】
文章目录一、实验目的二、实验要求三、实验内容四、实验报告要求一、实验目的 要求掌握Linux系统用户的创建、删除与管理操作;熟悉Linux文件系统的管理模式,学会创建用户文件系统并装载和卸载文件系统;掌握超级用户的管理方式与权限…...
sqlite3数据库-sqlite语句1(五)
DML(Data Manipulation Language,数据操作语言) SELECT:查询表中的数据;SELECT语句中使用WHERE子句SELECT <列名>,... FROM <表名> WHERE <条件表达式>;SELECT id,name,purchase_price FROM Product; /*使用逗号分隔查询多列,顺序同子句顺序*/ SELECT * FROM…...

【图像分类】卷积神经网络之LeNet5网络模型实现MNIST手写数字识别
写在前面: 首先感谢兄弟们的关注和订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。 在上一篇博文中我们对LeNet5网络模型的结构进行了剖析,本篇博文,我们将使用PyTorch搭建LeNet5实现MNIST手写数字…...

前端开发环境搭建
文章目录Node.js是什么安装查看版本入门示例NPM使用 npm 命令安装模块常见命令使用淘宝 NPM 镜像TypeScript安装入门示例从github拉取构建项目如何从零创建一个TypeScript项目规划目录结构新建项目Web App运行服务添加依赖打包使用browserify打包使用webpack打包推荐流程目录配…...
学习Flask之四、网页表单
第二章介绍的request对象,使用了客户端请求的所有信息。特别地,request.form提供了对POST请求提交的表单数据的访问。尽管Flask请求对象的支持足于处理网页单,但是还有很多作务很繁锁和重复。两个很好的例子是产生HTML表单代码和验证表单数据…...

CenterMask paper笔记
CenterMask是一个anchor free的实例分割模型, 来自paper: CenterMask: Real-Time Anchor-Free Instance Segmentation 提起anchor free, 会想到FCOS模型,是用来目标检测的, 那么这里就用到了FCOS, 不过换了backbone, 在FCOS检测出目标框后&…...

06- OpenCV查找图像轮廓 (OpenCV基础) (机器视觉)
知识重点 灰度图转换: gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)二值化: 返回两个东西,一个阈值, 一个是二值化的图: thresh, binary cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)查找轮廓: 返回两个结果,分别是轮廓和层级: c…...

OpenGL学习日记之模型绘制
自己编译运行过程中遇到的一些问题 下载Assimp已编译的lib(因为我们公司的电脑有很多权限和限制,也不能自己安装一些没有报备的软件,所以愁方便我就没有用cMake自己编译了)找到一位免费分享的博主的。 https://blog.csdn.net/lady_killer9/article/deta…...

Springboot接口多个DTO入参的Postman上传方式
在Java中使用Spring Boot框架时,可以同时使用多个DTO作为方法参数。 TO(Data Transfer Object)是一个常见的设计模式,用于封装数据传输对象。它通常用于将数据从一个层传递到另一个层,例如将数据从服务层传递到控制器…...
软考各科目考核内容详细介绍,看这里
新手在准备报考软考时,都会遇到这样的一个问题——科目这么多,我适合考什么?要想知道自己适合报什么科目,就需要了解每个科目是什么,考什么等一系列的问题。 接下来,就为大家介绍一下软考的各个科目&#…...
连续时间信号与离散时间信号
前言 《信号与系统》是一门很难的课,也是许多学校考研要考的专业课,由于每周只有两节课,所以每次上完都要及时的去复习,这里参考的教材是奥本海姆著作,刘海棠译,北京:电子工业出版社࿰…...

TPM密钥管理、使用
前面讲过证书相关内容,除了在软件方面有所应用外,在硬件方面也有很多应用。本次讲一下TPM相关的内容。 一、TPM介绍 1.1背景 TCG基于硬件安全的架构是为应对1990s后期日益增多的复杂恶意软件攻击应用而生的。当时以及现在,抵御PC客户端网络…...

return和finally执行顺序、运行时异常与一般异常异同、error和exception区别、Java异常处理机制原理与应用
文章目录1.try {}里有一个return语句,那么紧跟在这个try后的finally{}里的code会不会被执行,什么时候被执行,在return前还是后?2.运行时异常与一般异常有何异同?3.java 程序中的错误有三种类型分别是什么4.error和exception有什么…...

我为什么放弃WinUI3
基于WinUI3开发HiNote已经有一个多月的时间了,算是做出来一个简单能用的C端软件。 基于个人的经历,说说其中的开发体验。 UI设计语言 无论是否抄袭苹果,WinUI3给人的感觉都是眼前一亮的。简洁美观,现代化,毛玻璃的美…...
2023年全国最新安全员精选真题及答案2
百分百题库提供安全员考试试题、建筑安全员考试预测题、建筑安全员ABC考试真题、安全员证考试题库等,提供在线做题刷题,在线模拟考试,助你考试轻松过关。 21.(单选题)静作用压路机在施工过程,要求实际含水量…...

计算机408考研先导课---C语言难点
以下为小编在重温C语言时,容易犯错的一些点,希望列出来对大家有一定帮助! 一、整型变量数的范围 类型说明符长度(字节)数的范围int4/2(有些为4字节,有些为2字节)-32768~32767short2…...

K8S 部署 Redis-Cluster 集群
本文使用 bitnami 镜像部署 redis-cluster 官方文档:https://github.com/bitnami/charts/tree/main/bitnami/redis-cluster 添加 bitnami 仓库 helm repo add bitnami https://charts.bitnami.com/bitnami自定义 values.yaml storageClass:集群的存储…...

[oeasy]python0089_大型机的衰落_Dec小型机崛起_PDP_VAX网络
编码进化 回忆上次内容 上次 回顾了 计算机存储单位的演变 最小的读写单位 是 bit 8-bit 固定下来 成为了字节(Byte) 位数容量8-bit1Byte1024Byte1 KB1024 KB1 MB1024 MB1 GB1024 GB1 TB 存储字符时 第1位 是 标志位后7位 是 ascii具体的值 可以用 1Byte 存储 计算机之间 …...
《前端面试题:CSS对浏览器兼容性》
CSS浏览器兼容性完全指南:从原理到实战 跨浏览器兼容性是前端开发的核心挑战,也是面试中的高频考点。查看所有css属性对各个浏览器兼容网站:https://caniuse.com 一、浏览器兼容性为何如此重要? 在当今多浏览器生态中,…...

I2C通信讲解
I2C总线发展史 怎么在一条串口线上连接多个设备呢? 由于速度同步线是由主机实时发出的,所以主机可以按需求修改通信速度,这样在一条线上可以挂接不同速度的器件,单片机和性能差的器件通信,就输出较慢的脉冲信号&#x…...
在 CentOS 上将 Ansible 项目推送到 GitHub 的完整指南
1. 安装 Git 在 CentOS 中使用 yum 安装 Git,Git 是管理代码版本控制的工具: sudo yum install git -y 2. 配置 Git 用户信息 设置你的 Git 用户名和邮箱,这些信息会出现在你每次提交的记录中: git config --global user.nam…...

使用vite-plugin-html在 HTML 文件中动态注入数据,如元数据、环境变量、标题
vite-plugin-html 是一个用于 Vite 构建工具的插件,它可以帮助你在构建过程中动态注入一些 HTML 内容,比如标题、元数据、环境变量等。通过使用这个插件,你可以根据项目的配置和环境变量自动生成带有动态内容的 HTML 文件,适用于 …...

Python 打包指南:setup.py 与 pyproject.toml 的全面对比与实战
在 Python 开发中,创建可安装的包是分享代码的重要方式。本文将深入解析两种主流打包方法——setup.py 和 pyproject.toml,并通过一个实际项目示例,展示如何使用现代的 pyproject.toml 方法构建、测试和发布 Python 包。 一、setup.py 与 pyp…...

Authentication failed(切换了新的远程仓库tld)
启用 Git Credential Manager git config --global credential.helper manager 强制弹出凭据输入窗口 git config --global credential.helper.modalprompt true 指定 TFS 服务器使用基础认证(Basic Auth) git config --global credential.https://…...
2025.6.4总结
工作:今天效率比较高,早上回归4个问题,下午找了3个bug,晚上二刷了科目一(贪吃蛇系统),写了四个点,唯一没达标的就是两自动化没完成。美中不足的是电脑上下载不了PC版的番茄工作软件。…...

6个月Python学习计划 Day 14 - 异常处理基础( 补充学习)
第二周 Day 8 - Python 函数基础 Day 9 - 函数进阶用法 Day 10 - 模块与标准库入门 Day 11 - 列表推导式、内置函数进阶、模块封装实战 Day 12 - 字符串处理 & 文件路径操作 Day 13 - 文件操作基础 🎯 今日目标 理解异常的概念和常见异常类型掌握 try-except …...

[蓝桥杯]卡片换位
卡片换位 题目描述 你玩过华容道的游戏吗? 这是个类似的,但更简单的游戏。 看下面 3 x 2 的格子 --------- | A | * | * | --------- | B | | * | --------- 在其中放 5 张牌,其中 A 代表关羽,B 代表张飞,* …...
go语言学习 第5章:函数
第5章:函数 函数是编程中不可或缺的一部分,它封装了一段可重复使用的代码,用于执行特定的任务。在Go语言中,函数同样扮演着重要的角色。本章将详细介绍Go语言中函数的定义、调用、参数传递、返回值处理以及一些高级特性ÿ…...