【Unity3D小技巧】Unity3D中UI控制解决方案

news2025/3/10 21:35:02

推荐阅读

  • CSDN主页
  • GitHub开源地址
  • Unity3D插件分享
  • 简书地址
  • 我的个人博客

大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。

一、前言

在开发中总是会控制UI界面,如何优雅的控制UI界面是每一个Unity3D程序员的必修课。

这篇文章就总结了一下博主在实际开发中用到的几种控制UI的方式,分享出来以供批评指正。

在文章的最后,也根据UI控制做了一些延展,比如说:

  • 控制UI顺序
  • 控制UI层级
  • 控制初始化的先后顺序
  • 显示隐藏的堆栈

二、正文

2-1、讨论UI控制的解决方案

先说一下痛点吧,隐藏UI面板很简单,xx.SetActive(false);就行,但是这个管理的脚本放在哪里是个问题。

因为这个挂载的对象一旦隐藏,那么这个脚本就失灵了,所以一般不能挂载在UI面板自己身上,因为一旦隐藏就不管用了。

但是UI统一管理耦合性太高,不适合组件开发,但是也是一种控制方法。

挂载在UI面板自己身上,就需要一些技巧,避开隐藏自身这种行为。

由此为基础,有下面几种方案的讨论。

2-1-1、用一个脚本统一管理脚本的方式

实现方式
先搭建UI,然后新建一个对象挂载控制UI的脚本,然后这个脚本里面控制所有的UI事件。

例子
效果图:
在这里插入图片描述

在这里插入图片描述
代码参考:

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

public class UIControl : MonoBehaviour
{
    public GameObject Panel1;
    public Button Btn1;

    public GameObject Panel2;
    public Text TextTitle;
    public Text TextContent;

    public GameObject Panel3;
    public GameObject Panel4;

    void Start()
    {
        // 按钮绑定
        Btn1.onClick.AddListener(ClickSure);
    }

    void ClickSure()
    {

    }

    public void ShowPanel()
    {
        Panel1.SetActive(true);
    }
}

用这个脚本去控制所有的对象,保存隐藏显示、文字赋值、按钮绑定事件等。

优缺点
优点:
脚本一般都在一个独立对象上,脚本容易找。控制显示隐藏不容易报NULL对象错误。

缺点:
耦合性太高,所有的UI对象都在一个脚本中,脚本代码比较拥杂,并且无法独立出来形成组件进行复用。

2-1-2、面板自身挂载脚本,通过控制所有子对象来隐藏面板

实现方式
UI自身带有控制的脚本,通过控制所有子对象来实现隐藏或显示。

例子
效果图:
在这里插入图片描述

优缺点

优点:组件化开发,可以形成预制体复用,不必隐藏面板。

缺点:需要获取所有子对象,并且父节点身上不能添加Image,不然隐藏所有子对象也不行。

2-1-3、面板自身挂载脚本,通过控制UI界面缩放来隐藏面板

实现方式
UI界面挂载脚本,控制UI界面的缩放为0即可隐藏脚本,算是视觉隐藏,但是实际没有隐藏。

例子
效果图:
在这里插入图片描述

优缺点
优点:不必获取子对象,使用缩放控制。

缺点:不确定面板是否要显示,没法控制显示顺序。

2-1-4、面板自身挂载脚本,通过控制UI子节点来隐藏面板

实现方式
算是第一种和第二种方法的一种优化和升级。

在UI界面下面再设置一个节点用来控制所有的UI对象,随意控制隐藏和显示都没有问题,也不用获取所有的子对象,非常好用。

例子
效果图:
在这里插入图片描述

优缺点
优点:控制子对象,不用直接控制UI界面,避免脚本禁用情况,方便管理,也可以默认隐藏,更加灵活。

缺点:搭建UI的时候需要按照一定的规则搭建。比如UI界面是根节点,下一个节点是控制UI界面隐藏和显示的节点,再下面才是真正的UI搭建。

2-2、方法改良及可行性演示

2-1小结分析的几种解决方案都有优点和缺点,再次基础上,总结了一个比较完善的改良型方案。

UI面板自身挂载脚本,下面一个子节点是所有UI的父节点,也就是:

在这里插入图片描述
这样的话,一个UI界面的父节点是固定的,然后子节点用同一个名字方便脚本控制。

代码参考:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Diagnostics;
using UnityEngine.UI;

public class Panel3Logic : MonoBehaviour
{
    GameObject currentObj;
    Button BtnSure;
    Action endAction;

    void Start()
    {
        currentObj = transform.Find("UIControl").gameObject;
        currentObj.SetActive(false);

        BtnSure = currentObj.transform.Find("BtnSure").GetComponent<Button>();
        BtnSure.onClick.AddListener(BtnSureEvent);
    }

    /// <summary>
    /// 显示隐藏面板
    /// </summary>
    /// <param name="ison"></param>
    void ShowInfo(bool ison)
    {
        currentObj.SetActive(ison);
    }

    /// <summary>
    /// 设置委托函数
    /// </summary>
    /// <param name="ison"></param>
    /// <param name="endAction"></param>
    void ShowInfo(bool ison, Action endAction)
    {
        currentObj.SetActive(ison);
        this.endAction = endAction;
    }

    /// <summary>
    /// 点击确定的时候,关闭面板,并且执行委托函数
    /// </summary>
    void BtnSureEvent()
    {
        currentObj.SetActive(false);
        endAction?.Invoke();
    }
}

2-3、延展内容

这一节就将UI控制解决方案再做一下延展,包括:

  • 控制初始化的先后顺序
  • 控制UI层级和顺序
  • 显示隐藏的堆栈

新建一个UI基类UIBase.cs,双击编辑代码:

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

public class UIBase : MonoBehaviour
{
    /// <summary>
    /// 初始化顺序
    /// </summary>
    public int StartOrder;
    /// <summary>
    /// 层级顺序
    /// </summary>
    public int LayerOrder;
    /// <summary>
    /// 唯一标识符
    /// </summary>
    [HideInInspector]public int UniqueID;

    public virtual void OnStart() { }
    public virtual void ShowInfo(bool ison) { }
}

UI界面控制UI继承与这个基类,比如说Panel1Logic.cs,编辑代码:

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

public class Panel1Logic : MonoBehaviour
{
    GameObject currentObj;
    Button BtnSure;
    Action endAction;

    /// <summary>
    /// 初始化顺序
    /// </summary>
    public int StartOrder;
    /// <summary>
    /// 层级顺序
    /// </summary>
    public int LayerOrder;
    /// <summary>
    /// 唯一标识符
    /// </summary>
    public int UniqueID;

    public void OnStart()
    {
        currentObj = transform.Find("UIControl").gameObject;
        currentObj.SetActive(false);

        BtnSure = currentObj.transform.Find("BtnSure").GetComponent<Button>();
        BtnSure.onClick.AddListener(BtnSureEvent);
    }

    /// <summary>
    /// 显示隐藏面板
    /// </summary>
    /// <param name="ison"></param>
    public void ShowInfo(bool ison)
    {
        currentObj.SetActive(ison);
    }

    /// <summary>
    /// 设置委托函数
    /// </summary>
    /// <param name="ison"></param>
    /// <param name="endAction"></param>
    public void ShowInfo(bool ison, Action endAction)
    {
        currentObj.SetActive(ison);
        this.endAction = endAction;
    }

    /// <summary>
    /// 点击确定的时候,关闭面板,并且执行委托函数
    /// </summary>
    void BtnSureEvent()
    {
        currentObj.SetActive(false);
        endAction?.Invoke();
    }
}

UI控制脚本UIControl.cs,编辑代码:

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

public class UIControl : MonoBehaviour
{
    public List<UIBase> UIList;

    // 使用栈的后进先出的特性,实现UI的后显示先隐藏的功能
    Stack<GameObject> uiQueue;

    void Start()
    {
        // 初始化UI
        UIInit();
        // 初始化参数
        uiQueue = new Stack<GameObject>();
    }

    void UIInit()
    {
        // 排序
        UIList.Sort((x, y) => x.StartOrder.CompareTo(y.StartOrder));
        // 设置初始化顺序
        for (int i = 0; i < UIList.Count; i++)
        {
            Debug.Log(UIList[i].StartOrder);
            UIList[i].OnStart();
            UIList[i].UniqueID = 1000 + i;
        }
        // 排序
        UIList.Sort((x, y) => x.LayerOrder.CompareTo(y.LayerOrder));
        // 设置层级顺序
        for (int i = 0; i < UIList.Count; i++)
        {
            Debug.Log(UIList[i].LayerOrder);
            UIList[i].transform.SetSiblingIndex(UIList[i].LayerOrder);
        }
    }

    /// <summary>
    /// 显示UI
    /// </summary>
    /// <param name="UniqueID"></param>
    public void ShowPanel(int UniqueID)
    {
        UIBase uiObj = UIList.Find(value => value.UniqueID == UniqueID);
        if (uiObj != null)
        {
            uiObj.ShowInfo(true);
            uiQueue.Push(uiObj.gameObject);
        }
    }

    /// <summary>
    /// 隐藏UI 适用于多个UI重叠 点击任意位置关闭UI的情况
    /// </summary>
    public void HidePanel()
    {
        GameObject ui = uiQueue.Pop();
        ui.GetComponent<UIBase>().ShowInfo(false);
    }
}

三、后记

如果觉得本篇文章有用别忘了点个关注,关注不迷路,持续分享更多Unity干货文章。


你的点赞就是对博主的支持,有问题记得留言:

博主主页有联系方式。

博主还有跟多宝藏文章等待你的发掘哦:

专栏方向简介
Unity3D开发小游戏小游戏开发教程分享一些使用Unity3D引擎开发的小游戏,分享一些制作小游戏的教程。
Unity3D从入门到进阶入门从自学Unity中获取灵感,总结从零开始学习Unity的路线,有C#和Unity的知识。
Unity3D之UGUIUGUIUnity的UI系统UGUI全解析,从UGUI的基础控件开始讲起,然后将UGUI的原理,UGUI的使用全面教学。
Unity3D之读取数据文件读取使用Unity3D读取txt文档、json文档、xml文档、csv文档、Excel文档。
Unity3D之数据集合数据集合数组集合:数组、List、字典、堆栈、链表等数据集合知识分享。
Unity3D之VR/AR(虚拟仿真)开发虚拟仿真总结博主工作常见的虚拟仿真需求进行案例讲解。
Unity3D之插件插件主要分享在Unity开发中用到的一些插件使用方法,插件介绍等
Unity3D之日常开发日常记录主要是博主日常开发中用到的,用到的方法技巧,开发思路,代码分享等
Unity3D之日常BUG日常记录记录在使用Unity3D编辑器开发项目过程中,遇到的BUG和坑,让后来人可以有些参考。

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

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

相关文章

亚马逊精品广告推广怎么做?亚马逊怎么看精品广告推广?站斧浏览器

亚马逊精品广告推广怎么做? 对于想要在亚马逊平台上进行精品广告推广的商家来说&#xff0c;以下是一些常见的操作步骤和注意事项。 首先&#xff0c;商家需要在亚马逊广告平台注册并创建广告账户。亚马逊广告平台提供了专门的自助式广告管理工具&#xff0c;商家可以通过该…

数据结构-图的最小生成树

最小生成树介绍 最小生成树(Minimum Cost Spanning Tree)是代价最小的连通网的生成树&#xff0c;即该生成树上的边的权值和最小 最小生成树的性质&#xff1a; 必须使用且仅使用连通网中的n-1条边来联结网络中的n个顶点&#xff1b; 不能使用产生回路的边&#xff1b; 各…

linux vim 异常退出 异常处理 交换文件

交换文件 *.swp 格式 同时是隐藏的 如在vim一个文件&#xff0c; 在没有正常退出&#xff0c; 如直接断开连接 在次编辑这个文件 会出现下图的错误 解决方案&#xff1a; 直接删除这个交换文件即可 rm -fr .zen.txt.swp

css3 属性 backface-visibility 的实践应用

backface-visibility 是一个用于控制元素在面对屏幕不同方向时的可见性的CSS3特性。它有两个可能的值&#xff1a; visible&#xff1a;当元素不面向屏幕&#xff08;即背面朝向用户&#xff09;时&#xff0c;元素的内容是可以被看到的。hidden&#xff1a;当元素不面向屏幕…

【计算机网络】Socket的SO_REUSEADDR选项与TIME_WAIT

SO_REUSEADDR用于设置套接字的地址重用。当一个套接字关闭后&#xff0c;它的端口可能会在一段时间内处于TIME_WAIT状态&#xff0c;此时无法立即再次绑定相同的地址和端口。使用SO_REUSEADDR选项可以允许新的套接字立即绑定到相同的地址和端口&#xff0c;即使之前的套接字仍处…

如何使用Linux Archcraft结合内网穿透实现SSH远程连接

&#x1f4d1;前言 本文主要是使用Linux Archcraft结合内网穿透实现SSH远程连接的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是青衿&#x1f947; ☁️博客首页&#xff1a;CSDN主页放风讲故事 &#…

过年了,程序员们,请多关照自己!休息是为了走得更远!

文章目录 过年了&#xff0c;程序员们&#xff0c;请多关照自己&#xff01;一、理解“卷”背后的代价二、休息是为了走得更远三、关注健康&#xff0c;远离“过劳”四、平衡工作与生活&#xff0c;追求全面发展 过年了&#xff0c;程序员们&#xff0c;请多关照自己&#xff0…

input框前面名字长短不一时,让上下input框对齐方法

没设置之前 设置之后&#xff1a; 代码如下&#xff1a; <style>div{width: 500px;}label {display: block; /* 设置 label 元素为块级元素 */text-align: right; /* 设置文本右对齐 */margin-bottom: 10px; /* 设置标签之间的间距 */} </style> </head><…

Aigtek射频功率放大器有哪些具体应用

射频功率放大器是一种用于增加射频信号功率的电子器件。它在众多领域中有着广泛的具体应用&#xff0c;下面安泰电子将详细介绍几个主要的应用领域。 无线通信&#xff1a;射频功率放大器在无线通信系统中扮演着重要的角色。在移动通信领域&#xff0c;如蜂窝网络和卫星通信系统…

Solidworks 与 MATLAB 联合仿真

本文主要讲解了“MATLAB与SolidWorks的联合仿真怎么实现”&#xff0c;文中的讲解内容简单清晰&#xff0c;易于学习与理解&#xff0c;下面请大家跟着小编的思路慢慢深入&#xff0c;一起来研究和学习“MATLAB与SolidWorks的联合仿真怎么实现”吧&#xff01; 下载插件。 1、…

linux免密登录的实现

ssh免密登录使用方便&#xff0c;关键没有了口令验证反倒规避了暴力破解或者被探测的风险。配置得当&#xff0c;使用ssh免密登录更加安全。在生产环境中应用和数据库服务器之间互相设置后使用方便&#xff0c;并且在第三方人员配置使用时不用告知对方密码。 第一步、ssh登录发…

Python itertools模块:生成迭代器(实例分析)

itertools 模块中主要包含了一些用于生成迭代器的函数。在 Python 的交互式解释器中先导入 itertools 模块&#xff0c;然后输入 [e for e in dir(itertools) if not e.startswith(_)] 命令&#xff0c;即可看到该模块所包含的全部属性和函数&#xff1a; >>> [e for …

RabbitMQ控制台的基本使用

启动RabbitMQ后&#xff0c;浏览器 http://localhost:15672 打开RabbitMQ的控制台页面后&#xff0c;登录默认账户guest。 一. 添加队列 控制台选择队列&#xff0c;然后选择添加队列&#xff0c;队列类型默认经典类型&#xff0c;然后输入队列名称&#xff0c;最后添加队列。…

JSP和JSTL板块:第三节 JSP四大域对象 来自【汤米尼克的JAVAEE全套教程专栏】

JSP和JSTL板块&#xff1a;第三节 JSP四大域对象 一、page范围二、request范围三、session范围四、application范围 在服务器和客户端之间、各个网页之间、哪怕同一个网页之内&#xff0c;总是需要传递各种参数值&#xff0c;这时JSP的内置对象就是传递这些参数的载具。内置对象…

JeecgBoot jmreport/loadTableData RCE漏洞复现(CVE-2023-41544)

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

虫情监测设备能够自动识别病虫害

TH-CQ3S虫情监测设备的工作原理主要是通过高清摄像头拍摄农田的实时图像&#xff0c;利用图像识别技术对图像中的病虫害进行自动识别。一旦发现病虫害&#xff0c;设备会自动发出警报&#xff0c;并通过手机APP通知农民。农民可以根据设备提供的预测预报&#xff0c;及时采取防…

API攻击是什么?如何做好防范

API 攻击是针对应用程序接口的一种攻击手段&#xff0c;近年来逐渐成为网络安全领域的热点话题。攻击者主要针对应用程序接口中的漏洞或者错误进行API攻击&#xff0c;从而达到窃取敏感数据、进行恶意操作、破坏系统正常运行等恶意目的。 什么是API攻击&#xff1f; API 攻击是…

window 安装 jenkins 编写脚本

set JAVA_HOMED:\RuanJianKaiFa\jdk\jdk11 set CLASSPATH.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOMe%\lib\tools.jar; set Path%JAVA_HOME%\bin; java -jar jenkins.war 下载jenkins.war包&#xff0c;编写一个txt文档&#xff0c;把脚本复制进去&#xff0c;修改文件后缀为.bat文件…

【MySQL】MySQL库

使用C/C语言链接MySQL 一、mysql connect二、mysql 接口介绍1. 初始化 mysql_init()2. 链接数据库 mysql_real_connect()3. 执行 mysql 命令 mysql_query()4. 获取执行结果 mysql_store_result()5. 释放空间5. 关闭 mysql 链接 mysql_close() 一、mysql connect 要使用C语言连…

【JavaEE进阶】 图书管理系统开发日记——贰

文章目录 &#x1f332;前言&#x1f384;设计数据库&#x1f343;引⼊MyBatis和MySQL驱动依赖&#x1f333;Model创建&#x1f38d;约定前后端交互接口&#x1f340;服务器代码&#x1f6a9;控制层&#x1f6a9;业务层&#x1f6a9;数据层 &#x1f334;效果展示⭕总结 &#…