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 存储 计算机之间 …...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...

STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...

基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...

springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...