Unity UI框架

news2025/7/11 6:59:08

一、简介

最近在各大网站看了一下 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/360979.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux服务器+宝塔从头教你部署vue+springboot项目

前言 如果从没进行过将自己的个人项目部署到服务器的话&#xff0c;想要能够部署项目成功并且让别人访问到自己的项目还是比较难的。再加上我查找搜索别人的教程&#xff0c;要么就是互相复制抄步骤&#xff0c;要么就是不全&#xff0c;或者部署过程不明确&#xff0c;让人很…

Mybatis的介绍和基本使用

目录 数据库操作框架的历程 JDBC Hibernate JDBCTemplate 什么是Mybatis 快速搭建Mybatis项目 创建普通的maven项目 导入相关的依赖 创建对应的数据表 创建与表对应的实体类对象 创建对应的Mapper接口 编写配置文件 编写测试类 增删改查的基本操作 数据库操作框…

魔兽世界服务端端新手搭建教程

明杰也是很久以前开始研究魔兽服务器架设&#xff0c;主要原因是亚服经常要排队6-7个小时&#xff0c;去不排除的服和单机没啥区别&#xff0c;以怀旧服玩到10级以后就开始玩335端&#xff0c;一开始也和新入手的人一样云里雾里的&#xff0c;经过长时间的学习总算有点成就,向新…

三次握手四次挥手详细解析面试常问

文章目录1.第2次握手传回了ACK&#xff0c;为什么还要传回SYN&#xff1f;2.断开连接-TCP 四次挥手3.为什么要四次挥手&#xff1f;4.为什么不能把服务器发送的 ACK 和 FIN 合并起来&#xff0c;变成三次挥手&#xff1f;5.如果第二次挥手时服务器的 ACK 没有送达客户端&#x…

从零实现深度学习框架:Seq2Seq从理论到实战【理论篇】

来源&#xff1a;投稿 作者&#xff1a;175 编辑&#xff1a;学姐 往期内容&#xff1a; 从零实现深度学习框架1&#xff1a;RNN从理论到实战&#xff08;理论篇&#xff09; 从零实现深度学习框架2&#xff1a;RNN从理论到实战&#xff08;实战篇&#xff09; 从零实现深度…

RabbitMQ之Work Queues

Work Queues 工作队列(又称任务队列)的主要思想是避免立即执行资源密集型任务,而不得不等待它完成。相反我们安排任务在之后执行。我们把任务封装为消息并将其发送到队列。在后台运行的工作进程将弹出任务并最终执行作业。当有多个工作线程时,这些工作线程将一起处理这些任务…

Spring:五、编程式事务

Spring&#xff1a;五、编程式事务 1 前言 spring支持声明式和编程式事务&#xff0c;因spring事务基于AOP&#xff0c;使用cglib作为代理&#xff0c;为父子类继承的代理模式&#xff0c;故而声明式事务Transactional中&#xff0c;常见事务失效的场景&#xff0c;如方法内自…

基于UIAutomation+Python+Unittest+Beautifulreport的WindowsGUI自动化测试框架common目录解析

文章目录1 框架工具说明2 技术栈说明3 框架截图4 源码解析/common目录4.1 common/baseinfo.py4.2 common/creenShot.py4.3 common/logOut.py4.4 common/reportOut.py4.5 common/sendMail.py注&#xff1a; 1、本文为本站首发&#xff0c;他用请联系作者并注明出处&#xff0c;谢…

从源码中探究React中的虚拟DOM

引文 通过本文你将了解到 什么是虚拟DOM&#xff1f;虚拟DOM有什么优势&#xff1f;React的虚拟Dom是如何实现的&#xff1f;React是如何将虚拟Dom转变为真实Dom&#xff1f; 一、概念 虚拟DOM实际上是一种用来模拟DOM结构的javascript对象。当页面变化的时候通过一种算法来…

美国拟发布纽扣电池或硬币电池安全标准和通知要求ANSI C18. 3M

2023年2月10日&#xff0c;美国向WTO提交G/TBT/N/USA/1964号通报&#xff0c;拟发布纽扣电池或硬币电池以及含有此类电池的消费品的安全标准和通知要求&#xff0c;征求意见截止日期为2023年3月13日&#xff0c;拟通过日期和生效日期待定。联[1]系 拟定规则通知根据H.R.5313瑞…

微服务保护之sentinel熔断器

文章目录 目录 文章目录 前言 一、解决微服务雪崩的问题 二、使用步骤 三、熔断器的使用 3.1 限流规则 3.1.1流控模式 3.1.2流控效果 3.2 隔离和降级 3.2.1 隔离 3.2.2 降级 四、sentinel规则持久化 总结 前言 在基于 SpringCloud 构建的微服务体系中&#xff0c;服务间的调用…

宝塔搭建实战人才求职管理系统mobile手机端vue源码(五)

大家好啊&#xff0c;我是测评君&#xff0c;欢迎来到web测评。 上一期给大家分享骑士cms会员管理member前端vue在本地运行打包、宝塔发布部署的方式&#xff0c;本期给大家分享&#xff0c;mobile移动端vue怎么在本地运行&#xff0c;打包&#xff0c;实现线上功能更新替换的方…

南卡和wiwu电容笔哪款更值得入手?开学季电容笔推荐

在如今科技飞速发展的时代&#xff0c;数码产品层出不穷&#xff0c;尤其是电容笔。最近一段时间&#xff0c;电容笔非常受欢迎&#xff0c;很多人们都会使用电容笔来搭配平板电脑&#xff0c;不管在学习上或者工作上都随处可见。而随着电容笔的种类越来越多&#xff0c;人们对…

墨菲安全参与信息通信软件供应链安全社区成员大会并获自主研发创新成果奖

2023年2月16日&#xff0c;首届ICT软件供应链安全治理论坛暨信息通信软件供应链安全社区第二届成员大会在北京成功举办&#xff0c;多位业界顶级专家与工业和信息化部网络安全管理局相关领导出席&#xff0c;为现场观众分享了关于软件供应链可持续性与安全治理行业的前瞻与思考…

ICLR 2023|VLDet:从图像-文本对中学习区域-词语对齐的开放词汇式目标检测

原文链接&#xff1a;https://www.techbeat.net/article-info?id4614&isPreview1 作者&#xff1a;林闯 目标检测任务在AI工业界具有非常广泛的应用&#xff0c;但由于数据获取和标注的昂贵&#xff0c;检测的目标一直被限制在预先设定好的有限类别上。而在学术界&#xf…

2023最新软件测试面试题(带答案)

1. 请自我介绍一下(需简单清楚的表述自已的基本情况&#xff0c;在这过程中要展现出自信&#xff0c;对工作有激情&#xff0c;上进&#xff0c;好学) 面试官您好&#xff0c;我叫###&#xff0c;今年26岁&#xff0c;来自江西九江&#xff0c;就读专业是电子商务&#xff0c;毕…

OpenGL学习日记之模型绘制

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

聊聊8万8的私董会,很扎心

聊聊8万8的私董会&#xff0c;很扎心 道几句真心话&#xff0c;很扎心&#xff0c;但也很现实。 如果你喜欢刷抖音&#xff0c;这种感觉应该会更加明显。 股市哀鸿遍野&#xff0c;实体一地鸡毛&#xff0c;我们办公室楼下的门面换了一波又一波。 别说那些不起眼的小生意&a…

第48章 抽离Axios拦截守及其全局变量定义

1 准备工作 1.1 重构src\store\index.js import { createStore } from vuex; export default createStore({ state: { //通过该全局变量&#xff0c;获取全局化存储的1个指定用户的令牌字符串。 token: localStorage.getItem(Token) ? localStorage.getItem(Token) : , //通…

CHAPTER 3 Web Server - httpd配置(二)

Web Server - httpd配置二3.1 httpd配置3.1.1 基于用户的访问控制3.1.2 basic认证配置示例&#xff1a;1. 添加用户2. 添加网页文件3. 定义安全域4. 修改父目录权限5. 访问效果6. 在配置文件中定义一个".htaccess"隐藏文件7. 添加组3.1.3 虚拟主机1. 构建方案2. 基于…