【Unity小技巧】手戳一个简单易用的游戏UI框架(附源码)

文章目录

  • 前言
  • 整套框架分为三大部分
  • 框架代码
  • 调用
  • 源码
  • 参考
  • 完结

前言

开发一款游戏美术成本是极其高昂的,以我们常见的宣传片CG为例,动辄就要成百上千万的价格,因此这种美术物料一般只会放在核心剧情节点,引爆舆论,做高潮展示!

而另外一种表意方武:则是通过玩法设计,层层导玩家深入探讨游戏世界,这里则需要策划的精心设计和程序的秃头爆肝,因此对于绝大多数游戏而言,选择UI来进行剧情展示、玩法交互和核心表达才是最物美价廉的选择。

unity在4.6版本之后,引入了自己的界面显示系统,全称unity graphic user interface,即我们所熟知的ugui,毕竟是unity的亲儿子,这个系统一经推出,就与其灵活快速可视化。
在这里插入图片描述
迅速抢占用户市场,逐渐成为unity ui的主流系统,但是它也并不是完美的,对于开发人员来说,使用这套系统往往需要面对如下困境,比如缺乏跨场景的u管理器,界面的上下层关系紊乱三,界面之间的通信手段贫乏等等,上述几个问题大大影响到我们的开发效率。
在这里插入图片描述
上述几个问题大大影响到我们的开发效率,因此针对上述问题,我在此分享一套简单易用的框架,这套框架是b站up小棋参考了网上的大量资料,并结合个人工作经验,一个代码一个代码敲出来的,绝对是良心干货,所以废话少说,让我们开始吧!

整套框架分为三大部分

在这里插入图片描述
在游戏运行时,我们会把界面配置关系配置在UIManager中,当我们创建好了一个界面,比如用户列表界面,那么我们就给这个界面取一个名,并且把它放在prefab下的一个文件夹,并且配置在这个配置关系表中,后面我们在新创建一些界面,比如菜单界面,也可以把它配置在这个界面关系配置表中

我总共使用了三个界面来举例,最后一个界面是创建新用户的一个界面,总共配了三个配置关系,包含名称、路径和预制体
在这里插入图片描述
配置完成后,我们给UIManager提供两个最重要的方法,一个是OpenPanel和ClosePanel,用于打开界面和关闭界面,介绍完框架的设计
我们就可以开始书写代码了

打开游戏编辑器,我们首先在一个空场景中创建canvas画布,并且将其参数设置为合适的范围和类型,这个主要根据自己项目来设置

接着我们使用工程中的ui素材拼接界面,我这里分了如下几个界面,主菜单界面,用户列表界面和新用户界面等等,最后把这些界面拖拽到resources下的某个目录,将其制作成预制件执行
在这里插入图片描述
加入代码后,最终的运行效果就是这样的

框架代码

UIManager.cs

using System.Collections;
using System.Collections.Generic;
using System;
using UnityEngine;
// 用户界面管理器
public class UIManager
{
    // 单例模式的实例
    private static UIManager _instance;
    // 用户界面的根节点
    private Transform _uiRoot;
    // 路径配置字典
    private Dictionary<string, string> pathDict;
    // 预制件缓存字典
    private Dictionary<string, GameObject> prefabDict;
    // 已打开界面的缓存字典
    public Dictionary<string, BasePanel> panelDict;

    // 获取单例模式的实例
    public static UIManager Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new UIManager();
            }
            return _instance;
        }
    }

    // 获取用户界面的根节点
    public Transform UIRoot
    {
        get
        {
            if (_uiRoot == null)
            {
                if (GameObject.Find("Canvas"))
                {
                    _uiRoot = GameObject.Find("Canvas").transform;
                }
                else
                {
                    _uiRoot = new GameObject("Canvas").transform;
                }
            };
            return _uiRoot;
        }
    }

    // 用户界面管理器的构造函数
    private UIManager()
    {
        InitDicts();
    }

    // 初始化字典
    private void InitDicts()
    {
        prefabDict = new Dictionary<string, GameObject>();
        panelDict = new Dictionary<string, BasePanel>();

        pathDict = new Dictionary<string, string>()
        {
            {UIConst.AllCardPanel, "Menu/AllCardPanel"},
            {UIConst.MenuPanel, "Menu/MenuPanel"},
            {UIConst.MainMenuPanel, "Menu/MainMenuPanel"},
            {UIConst.MenuTipPanel, "Menu/MenuTipPanel"},
            {UIConst.NewUserPanel, "Menu/NewUserPanel"},
            {UIConst.UserPanel, "Menu/UserPanel"},
            {UIConst.ReNameUserPanel, "Menu/ReNameUserPanel"},
        };
    }

    // 打开面板
    public BasePanel OpenPanel(string name)
    {
        BasePanel panel = null;
        // 检查是否已打开
        if (panelDict.TryGetValue(name, out panel))
        {
            Debug.Log("界面已打开: " + name);
            return null;
        }

        // 检查路径是否配置
        string path = "";
        if (!pathDict.TryGetValue(name, out path))
        {
            Debug.Log("界面名称错误,或未配置路径: " + name);
            return null;
        }

        // 使用缓存预制件
        GameObject panelPrefab = null;
        if (!prefabDict.TryGetValue(name, out panelPrefab))
        {
            string realPath = "Prefab/Panel/" + path;
            panelPrefab = Resources.Load<GameObject>(realPath) as GameObject;
            prefabDict.Add(name, panelPrefab);
        }

        // 打开界面
        GameObject panelObject = GameObject.Instantiate(panelPrefab, UIRoot, false);
        panel = panelObject.GetComponent<BasePanel>();
        panelDict.Add(name, panel);
        panel.OpenPanel(name);
        return panel;
    }

    // 关闭面板
    public bool ClosePanel(string name)
    {
        BasePanel panel = null;
        if (!panelDict.TryGetValue(name, out panel))
        {
            Debug.Log("界面未打开: " + name);
            return false;
        }

        panel.ClosePanel();
        // panelDict.Remove(name);
        return true;
    }

    // 显示提示
    public void ShowTip(string tip)
    {
        MenuTipPanel menuTipPanel = OpenPanel(UIConst.MenuTipPanel) as MenuTipPanel;
        menuTipPanel.InitTip(Globals.TipCreateOne);
    }
}

// 用户界面常量
public class UIConst
{
    // menu panels
    public const string AllCardPanel = "AllCardPanel";
    public const string MenuPanel = "MenuPanel";
    public const string MainMenuPanel = "MainMenuPanel";
    public const string MenuTipPanel = "MenuTipPanel";
    public const string NewUserPanel = "NewUserPanel";
    public const string UserPanel = "UserPanel";
    public const string ReNameUserPanel = "ReNameUserPanel";
}

BasePanel.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BasePanel : MonoBehaviour  // 基础面板类,继承自MonoBehaviour
{
    protected bool isRemove = false;  // 是否移除,默认为否
    protected new string name;  // 面板名称

    public virtual void SetActive(bool active)  // 设置面板活动状态
    {
        gameObject.SetActive(active);  // 设置游戏对象活动状态
    }

    public virtual void OpenPanel(string name)  // 打开面板方法
    {
        this.name = name;  // 设置面板名称
        SetActive(true);  // 设置面板为活动状态
    }

    public virtual void ClosePanel()  // 关闭面板方法
    {
        isRemove = true;  // 设置移除状态为是
        SetActive(false);  // 设置面板为非活动状态
        Destroy(gameObject);  // 销毁游戏对象
        if (UIManager.Instance.panelDict.ContainsKey(name))  // 如果UI管理器的面板字典包含该面板
        {
            UIManager.Instance.panelDict.Remove(name);  // 从字典中移除该面板
        }
    }
}

调用

以我视频中所示的这些界面为例,每一个界面我都会给他们挂接上一个脚本,这些脚本继承自base panel,比如在主菜单界面,当我点击修改用户时,则会弹出用户列表的这个界面,这时候我只需要调用UIManager.Instance.OpenPanel,然后打开这个界面即可

public void OnBtnChangeUser()
{
	print("OnBtnChangeUser");
	UIManager.Instance.OpenPanel(UIConst.UserPanel);
}

关闭界面,也是类似的道理,只要调用close panel即可

public void OnBtnCancel(){
	print("OnBtnRename");
	ClosePanel();
}

源码

整理好我会放上来

参考

【视频】https://www.bilibili.com/video/BV1zT411b7L3/

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,出于兴趣爱好,于是最近才开始自习unity。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

RabbitMQ---Spring AMQP

Spring AMQP 1. 简介 Spring有很多不同的项目&#xff0c;其中就有对AMQP的支持&#xff1a; Spring AMQP的页面&#xff1a;http://spring.io/projects/spring-amqp 注意这里一段描述&#xff1a; Spring-amqp是对AMQP协议的抽象实现&#xff0c;而spring-rabbit 是对协…

detour编译问题及导入visual studio

Detours是经过微软认证的一个开源Hook库&#xff0c;Detours在GitHub上&#xff0c;网址为 https://github.com/Microsoft/Detours 注意版本不一样的话也是会出问题的&#xff0c;因为我之前是vs2022的所以之前的detours.lib不能使用&#xff0c;必须用对应版本的x64 Native To…

项目:点餐系统3mysql知识回顾MySQL客户端

连接数据库 mysql -uroot -p 密码&#xff1a;空 一、第三方库&#xff1a;MySQL 数据库-存储并管理数据的仓库&#xff0c;是一个C/S架构 MySQL客户端通过sql来告诉MySQL服务器&#xff0c;自己需要做什么操作 1.sql语句 sql&#xff1a;structure query language结构化查询…

getKernel32Address自实现(x64下写法)

HMODULE getKernel32Address() {PVOID64 Peb GetPeb();PVOID64 LDR_DATA_Addr *(PVOID64**)((BYTE*)Peb 0x018); //0x018是LDR相对于PEB偏移 存放着LDR的基地址UNICODE_STRING* FullName;HMODULE hKernel32 NULL;LIST_ENTRY* pNode NULL;pNode (LIST_ENTRY*)(*(PVOID6…

Python Opencv实践 - Canny边缘检测

import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/pomeranian.png", cv.IMREAD_GRAYSCALE) print(img.shape)#图像Canny边缘检测 #cv.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradien…

C++标准库STL容器详解

目录 C标准模板库STL容器容器分类容器通用接口 顺序容器vectorlistdeque 容器适配器queuestackpriority_queue 关联容器&#xff1a;红黑树setmultisetmapmultimap 关联容器&#xff1a;哈希表unordered_set和unordered_multisetunordered_map和unordered_multimap 附1&#xf…

C语言——类型转换

数据有不同的类型&#xff0c;不同类型数据之间进行混合运算时涉及到类型的转换问题。 转换的方法有两种&#xff1a; 自动转换(隐式转换)&#xff1a;遵循一定的规则&#xff0c;由编译系统自动完成强制类型转换&#xff1a;把表达式的运算结果强制转换成所需的数据类型 语法格…

c++(8.29)auto关键字,lambda表达式,数据类型转换,标准模板库,list,文件操作+Xmind

作业&#xff1a; 封装一个学生的类&#xff0c;定义一个学生这样类的vector容器, 里面存放学生对象&#xff08;至少3个&#xff09; 再把该容器中的对象&#xff0c;保存到文件中。 再把这些学生从文件中读取出来&#xff0c;放入另一个容器中并且遍历输出该容器里的学生。…

小白视角:一文读懂3TS腾讯事务处理验证系统的基础知识

小白视角&#xff1a;一文读懂3TS腾讯事务处理验证系统的基础知识 一、解读结果图1.1 异常测试用例1.2 事务的隔离级别&#xff08;1&#xff09;SQL标准隔离级别&#xff08;2&#xff09;快照隔离&#xff08;Snapshot Isolation&#xff0c;简称 SI&#xff09;&#xff08;…

【单片机】有人WH-LTE-7S1 4G cat1 模块连接服务器,教程,记录

文章目录 4G cat1 模块封装引脚名称功能拓扑图串口模块调试WH-LTE-7S1 4G cat1 模块 我买的这个模块内置了电信卡&#xff0c;不用插电话卡就能用&#xff0c;要插也行&#xff0c;在背面。 ⚫ 5-16V 宽电压供电 ⚫ LTE Cat 1&#xff0c;搭载 4G 网络&#xff0c;低时延&…

SQL注入之报错注入

文章目录 报错注入是什么&#xff1f;报错注入获取cms账号密码成功登录 报错注入是什么&#xff1f; 在注入点的判断过程中&#xff0c;发现数据库中SQL 语句的报错信息&#xff0c;会显示在页面中&#xff0c;因此可以利用报错信息进行注入。 报错注入的原理&#xff0c;就是在…

虚拟化技术:云计算发展的核心驱动力

文章目录 虚拟化技术的概念和作用虚拟化技术的优势虚拟化技术对未来发展的影响结论 &#x1f389;欢迎来到AIGC人工智能专栏~虚拟化技术&#xff1a;云计算发展的核心驱动力 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&#xff1a;IT陈寒的博客&#x1f388;该系…

Particle Life粒子生命演化的MATLAB模拟

Particle Life粒子生命演化的MATLAB模拟 0 前言1 基本原理1.1 力影响-吸引排斥行为1.2 距离rmax影响 2 多种粒子相互作用2.1 双种粒子作用2.1 多种粒子作用 3 代码 惯例声明&#xff1a;本人没有相关的工程应用经验&#xff0c;只是纯粹对相关算法感兴趣才写此博客。所以如果有…

C#: Json序列化和反序列化,集合为什么多出来一些元素?

如下面的例子&#xff0c;很容易看出问题&#xff1a; 如果类本身的无参构造函数&#xff0c; 就添加了一些元素&#xff0c;序列化&#xff0c;再反序列化&#xff0c;会导致元素增加。 如果要避免&#xff0c;必须添加&#xff1a; new JsonSerializerSettings() { Object…

Node爬虫项目精简版 wallhaven网站实操 2023.8.29

练习地址&#xff1a; https://wallhaven.cc/toplist const express require(express); const axios require(axios); const cheerio require(cheerio); const schedule require(node-schedule); const fs require(fs);async function downloadImage(url) {const response…

SpringBoot案例-基础登录功能

根据页面原型&#xff0c;明确需求 页面原型 需求 账号密码输入正确方可进入 阅读接口文档 接口文档连接如下&#xff1a; https://hkm-web.oss-cn-beijing.aliyuncs.com/%E6%8E%A5%E5%8F%A3%E6%96%87%E6%A1%A3 思路分析 后端接收到前端传递的用户名及密码之后&#xf…

Unity打包Windows程序,概率性出现无法全屏或分辨率不匹配

排除代码和Resolution and Presentation面板设置问题 如果程序还是不能按照预期的分辨率运行&#xff0c;应该是系统注册表记录了对应的设置。 解决方案&#xff1a; 打开注册表&#xff0c;使用快捷键“Win” "R"组合快捷键。在打开后面键入命令&#xff1a;Rege…

安装Ubuntu服务器、配置网络、并安装ssh进行连接

安装Ubuntu服务器、配置网络、并安装ssh进行连接 1、配置启动U盘2、配置网络3、安装ssh4、修改ssh配置文件5、重启电脑6、在远程使用ssh连接7、其他报错情况 1、配置启动U盘 详见: U盘安装Ubuntu系统详细教程 2、配置网络 详见&#xff1a;https://blog.csdn.net/davidhzq/a…

简易虚拟培训系统-UI控件的应用3

目录 Button组件的组成 Button组件方法1-在Button组件中设置OnClick()回调 Button组件方法2-在脚本中添加Button类的监听 上一篇使用了文件流读取硬盘数据并显示在Text组件中&#xff0c;本篇增加使用按钮来控制显示哪一篇文字信息。 Button组件的组成 1. 新建Button&#…

Node.js crypto模块 加密算法

背景 微信小程序调用飞蛾热敏纸打印机&#xff0c;需要进行参数sig签名校验&#xff0c;使用的是sha1进行加密 // 通过crypto.createHash()函数&#xff0c;创建一个hash实例&#xff0c;但是需要调用md5&#xff0c;sha1&#xff0c;sha256&#xff0c;sha512算法来实现实例的…