当前位置: 首页 > news >正文

UnityUI系统--GUI

认识GUI

IMGUI 是 Unity 中一种独特、强大且历史悠久的 GUI 系统,它的“即时”性质使其在​​代码驱动​​、​​快速迭代​​和​​状态透明​​的场景中表现出色,尤其是在 ​​Unity 编辑器扩展开发​​中扮演着核心角色。然而,其每帧重绘的模型带来的性能开销和可视化/复杂布局能力的欠缺,使它​​不适合​​构建现代游戏中主流的、性能敏感的、设计精美的用户界面。对于游戏 UI,​​UGUI​​ 和更新推出的 ​​UI Toolkit​​ 是更合适的选择。理解 IMGUI 的核心概念对于扩展 Unity 编辑器和创建调试工具至关重要。​

IMGUI 与传统 GUI 系统(如 UGUI 的事件驱动模式)有本质区别:

  1. ​无持久状态对象:​​ IMGUI 不像 UGUI 那样预先创建并维护 ButtonLabelSlider 等持久的 UI 对象存在于场景中。UI 元素是“即时”绘制的。
  2. ​函数驱动:​​ 你通过在 OnGUI() 方法(或类似的编辑器窗口 OnGUI 方法)中调用一系列 GUI 或 GUILayout 类的函数(如 GUI.Button()GUILayout.Label()GUI.Toggle())来声明每一帧要显示的 UI。
  3. ​响应即是返回值:​​ 这些函数不仅绘制控件,更重要的是它们​​直接返回用户的交互状态​​。例如:
    • bool clicked = GUI.Button(rect, "Click Me"); 在绘制按钮的同时,clicked 会在按钮被点击的那一帧返回 true
    • string enteredText = GUI.TextField(rect, currentText); 在绘制输入框的同时,返回用户输入/更新后的文本。
  4. ​基于每帧:​​ OnGUI() 方法在每一帧都会被调用(在渲染之前)。整个 UI 是在每一帧中从头开始“绘制”(或更准确地说,声明应该如何绘制)并处理交互的。这意味着 UI 逻辑需要高效。

​核心特性与优缺点​

  • ​优点:​
    • ​简单快速原型:​​ 无需设置场景、预制体或编辑器拖拽,几行代码就能创建出调试按钮、滑块或文本字段。非常适合临时信息显示和快速测试。
    • ​工具开发的黄金标准:​​ IMGUI 是 Unity 编辑器本身扩展(如自定义 Inspector 面板、Editor 窗口、Scene 视图工具)的​​官方首选 API​​。UnityEditor 命名空间下的 EditorGUI/EditorGUILayout 类都是基于 IMGUI 构建的扩展,提供了大量编辑器专用控件。
    • ​无状态管理:​​ 因为 UI 是即时绘制的,控件不保存自己的状态(开/关,当前值等)。状态完全由​​你的代码​​管理(存储为 MonoBehaviour 的成员变量或 EditorWindow 的变量)。这简化了一些复杂逻辑,状态逻辑完全透明。
    • ​深度集成:​​ 在编辑器扩展中,与 Unity 原生对象(如 SerializedObject/SerializedProperty)的集成非常紧密和直接。
    • ​完全代码控制:​​ 对程序化生成高度动态或极其复杂的界面有独特优势(只要性能不是瓶颈)。
  • ​缺点:​
    • ​性能:​​ 最大的缺点。每帧都要重新绘制和处理整个 UI 层次结构(即使没有变化),在 UI 复杂或游戏运行在较低性能设备(移动端)时,可能带来显著开销。​​绝对不适合游戏内复杂UI或移动端。​
    • ​难以构建复杂、动态 UI:​​ 创建具有复杂布局、动画、动态添加/移除元素的大型、可维护的用户界面非常笨拙和困难。UGUI/UI Toolkit 更擅长于此。
    • ​缺乏可视化设计:​​ 所有 UI 元素的位置、大小、样式都通过代码定义(矩形 Rect),没有所见即所得(WYSIWYG)的编辑器。调整布局需要修改代码并重新运行。
    • ​样式有限:​​ 内置控件的默认样式比较基础。虽然可以自定义外观(如使用自定义纹理 GUIStyleGUISkin),但这需要额外代码工作,且不如 UGUI/UI Toolkit 的图形化样式设置直观。
    • ​布局挑战:​​ 手动计算所有 Rect (Rect(x, y, width, height)) 来精确定位是繁琐且不灵活的。虽然 GUILayout 系列函数提供了一种基于“自动布局区域”的方式(类似于 HTML 的流式布局),节省了手动计算 Rect 的工作,但其布局控制仍然不如 UGUI 的 RectTransform 锚点系统和布局组件强大和直观。复杂的 GUILayout 嵌套也可能导致意外行为。

​核心类和方法​

  • GUI 类:​​ 核心静态类。包含​​手动定位 (Rect)​​ 的基本控件函数。你需要精确指定每个控件的位置和大小。
    • GUI.Button(Rect position, string text/Texture): 绘制按钮,点击返回 true
    • GUI.Label(Rect position, string text): 绘制只读文本标签。
    • GUI.Box(Rect position, string text/Texture): 绘制背景框。
    • GUI.TextField(Rect position, string text): 绘制单行文本输入框,返回输入文本。
    • GUI.Toggle(Rect position, bool value, string text/Texture): 绘制开关按钮,返回开关状态。
    • GUI.HorizontalSlider/Rect position, float value, float leftValue, float rightValue): 绘制水平滑动条,返回滑块值。
    • GUI.DrawTexture(Rect position, Texture2D texture): 在指定位置绘制纹理。
    • GUI.Window(int id, Rect clientRect, GUI.WindowFunction func, string title): 绘制一个可拖动的窗口。func 是一个委托方法,内部绘制该窗口的内容。
  • GUILayout 类:​​ 基于 GUI 的静态类。提供​​自动布局​​版本的控件。你只需指定控件内容和一些选项(如宽度/高度约束),系统会​​自动计算位置和尺寸​​(从上到下,从左到右排列)。
    • GUILayout.Button(string text/Texture, params GUILayoutOption[] options)
    • GUILayout.Label(string text, params GUILayoutOption[] options)
    • GUILayout.Box(string text/Texture, params GUILayoutOption[] options)
    • GUILayout.TextField(string text, params GUILayoutOption[] options)
    • GUILayout.Toggle(bool value, string text/Texture, params GUILayoutOption[] options)
    • GUILayout.HorizontalSlider(float value, float leftValue, float rightValue, params GUILayoutOption[] options)
    • 布局控制函数:
      • GUILayout.BeginHorizontal() / GUILayout.EndHorizontal(): 开始/结束一个水平自动布局组。
      • GUILayout.BeginVertical() / GUILayout.EndVertical(): 开始/结束一个垂直自动布局组。
      • GUILayout.FlexibleSpace(): 插入一个可伸缩的空间,将控件推到一边。
      • GUILayout.Space(float pixels): 插入固定像素高度的空格。
      • GUILayoutOption: 如 GUILayout.Width(float)GUILayout.Height(float)GUILayout.MinWidth(float)GUILayout.ExpandWidth(bool) 等,用于在 GUILayout 控件中约束大小。
  • GUIStyle:​​ 定义单个控件的外观(字体、对齐方式、颜色、边距、背景图等)。可以创建并应用于特定的 GUI/GUILayout 调用。
  • GUISkin:​​ 包含一组预定义的 GUIStyle(如 buttonlabeltextField 的默认样式)。可以一次性应用到整个 UI 层级。
  • GUIUtility 和 GUIContent:​​ 提供实用工具(如获取鼠标在 GUI 坐标中的位置)和一种将文本、图像、工具提示封装在一起传递给控件的方式。

​何时使用 IMGUI?​

  1. ​Unity 编辑器扩展开发 (Editor Scripting):​​ 这是 ​​IMGUI 目前最主要且推荐的使用场景​​。自定义 Inspector、自定义 Editor 窗口、Scene 视图控件(Handle)、Game 视图工具栏扩展等都离不开它。
  2. ​调试界面与运行时工具:​
    • 快速在游戏视图顶部叠加调试信息(如 FPS、内存、内部状态变量)。
    • 临时添加控制面板(开关特效、调整参数、触发测试事件)。
    • 内置作弊控制台 (Cheat Console)。
  3. ​游戏原型设计:​​ 在项目早期,快速搭建起简单的菜单或设置界面以测试核心玩法逻辑,而无需投入 UI 美术资源或搭建 UGUI 结构。
  4. ​极其动态或程序化 UI:​​ 如果 UI 是高度动态生成且结构极其复杂(难以用 UGUI 预制体管理),完全由算法控制,且性能不是首要关注点(如 PC 上的工具软件)。

​何时避免使用 IMGUI?​

  1. ​游戏内主要的、复杂的 UI:​​ 如主菜单、HUD、设置菜单、背包系统、对话系统等。请使用 ​​UGUI​​ 或 ​​UI Toolkit​​。
  2. ​性能敏感场景:​​ 尤其是移动端游戏。
  3. ​需要丰富动画和视觉效果的 UI。​
  4. ​需要可视化编辑器和所见即所得设计体验的项目。​
  5. ​需要大量布局控制且期望自动适配不同分辨率/屏幕比例的 UI。​

封装GUI中常见的组件,让GUI可以所见即所得

系统总结​

这些脚本共同构建了一个​​自定义GUI框架​​:

  1. ​基础结构​​:
    CustomGUIControl 提供抽象基类,CustomGUIPos 解决布局问题,CustomGUIRoot 实现统一绘制。
  2. ​控件库​​:
    包含按钮、输入框、标签、滑动条、图片、开关等常用UI组件。
  3. ​事件驱动​​:
    控件通过 UnityEvent 暴露交互事件(点击、文本变化、值变化等)。
  4. ​扩展性​​:
    通过继承 CustomGUIControl 可快速创建新控件。
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.Rendering;public enum E_Aligent_Type
{Up,Down,Left, Right,Center,Left_Up,Right_Up,Left_Down,Right_Down,
}
[System.Serializable]
public class CustomGUIPos
{private Rect rPos = new Rect(0, 0, 100, 100);public E_Aligent_Type screen_Aligent_Type;public E_Aligent_Type control_Aligent_Type;//用户自行偏移位置public Vector2 pos;//屏幕的宽高public float Width=100;public float Height=50;private Vector2 centerPos;private void CalcCenterPos(){switch (control_Aligent_Type){case E_Aligent_Type.Up:centerPos .x=-Width /2;centerPos .y=0;break;case E_Aligent_Type.Down:centerPos .x=-Width /2;centerPos .y=Height;break;case E_Aligent_Type.Left:centerPos .x=0;centerPos .y=-Height /2;break;case E_Aligent_Type.Right:centerPos .x=-Width;centerPos .y=-Height/2;break;case E_Aligent_Type.Center:centerPos .x=-Width /2;centerPos .y=-Height/2 ;break;case E_Aligent_Type.Left_Up:centerPos .x=0;centerPos .y=0;break;case E_Aligent_Type.Right_Up:centerPos .x=-Width;centerPos .y=0;break;case E_Aligent_Type.Left_Down:centerPos .x=0;centerPos .y=-Height;break;case E_Aligent_Type.Right_Down:centerPos .x=-Width;centerPos .y=-Height;break;}}private void CalcControlPos(){switch (screen_Aligent_Type){case E_Aligent_Type.Up:rPos.x = Screen.width / 2 + centerPos.x + pos.x;rPos.y = centerPos.y + pos.y;break;case E_Aligent_Type.Down:rPos.x = Screen.width / 2 + centerPos.x + pos.x;rPos.y = Screen.height + centerPos.y - pos.y;break;case E_Aligent_Type.Left:rPos.x = centerPos.x + pos.x;rPos.y = Screen.height / 2 + centerPos.y + pos.y;break;case E_Aligent_Type.Right:rPos.x = Screen.width + centerPos.x - pos.x;rPos.y = Screen.height / 2 + centerPos.y + pos.y;break;case E_Aligent_Type.Center:rPos.x = Screen.width / 2 + centerPos.x + pos.x;rPos.y = Screen.height / 2 + centerPos.y + pos.y;break;case E_Aligent_Type.Left_Up:rPos.x = centerPos.x + pos.x;rPos.y = centerPos .y+pos.y;break;case E_Aligent_Type.Right_Up:rPos .x=Screen .width +centerPos .x - pos.x;rPos .y =centerPos .y+pos.y;break;case E_Aligent_Type.Left_Down:rPos .x = centerPos.x + pos.x;rPos .y =Screen .height + centerPos .y - pos.y;break;case E_Aligent_Type.Right_Down:rPos .x=Screen .width +centerPos .x - pos.x;rPos .y=Screen .height + centerPos .y - pos.y;break;}}public Rect Pos{get{CalcCenterPos();CalcControlPos();rPos.width = Width;rPos.height = Height;return rPos;}}}

CustomGUIPos.cs​

  • ​功能​​:​​屏幕位置计算工具​​。
  • ​核心特性​​:
    • 通过 E_Aligent_Type 枚举定义控件在屏幕上的对齐方式(9种,如左上、居中)。
    • 动态计算控件矩形区域 (Rect),考虑屏幕尺寸、控件尺寸、自定义偏移 (pos)。
    • 通过 Pos 属性返回最终位置(自动调用 CalcCenterPos() 和 CalcControlPos())。
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using UnityEngine;public enum E_Style_OnOff
{On,Off
}public abstract class CustomGUIControl : MonoBehaviour
{public CustomGUIPos guiPos;public GUIStyle guiStyle;public GUIContent guiContent;public E_Style_OnOff styleOnOff=E_Style_OnOff.Off;public void DrawGUI(){switch (styleOnOff){case E_Style_OnOff.On:StyleOnDraw();break;case E_Style_OnOff.Off:StyleOffDraw();break;default:break;}}protected abstract void StyleOnDraw();protected abstract void StyleOffDraw();
}

 CustomGUIControl.cs​

  • ​功能​​:自定义GUI控件的​​抽象基类​​。
  • ​核心特性​​:
    • 管理控件位置 (guiPos)、样式 (guiStyle)、内容 (guiContent)。
    • 通过 E_Style_OnOff 枚举控制是否启用GUIStyle。
    • 提供 DrawGUI() 方法,根据样式开关调用 StyleOnDraw() 或 StyleOffDraw()(需子类实现)。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;[ExecuteAlways]
public class CustomGUIRoot : MonoBehaviour
{private CustomGUIControl []allControls;// Start is called before the first frame updatevoid Start(){allControls = this.GetComponentsInChildren<CustomGUIControl>();}private void OnGUI(){if(!Application .isPlaying){allControls = this.GetComponentsInChildren<CustomGUIControl>();}for (int i = 0; i < allControls .Length; i++){allControls[i].DrawGUI();}}
}

CustomGUIRoot.cs​

  • ​功能​​:​​GUI控件根节点管理器​​。
  • ​核心特性​​:
    • 使用 [ExecuteAlways] 在编辑模式和运行时生效。
    • 自动收集子节点的 CustomGUIControl 组件。
    • 在 OnGUI() 中统一绘制所有子控件。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class CustomGUILabel : CustomGUIControl
{protected override void StyleOffDraw(){GUI.Label(guiPos.Pos, guiContent);}protected override void StyleOnDraw(){GUI.Label(guiPos.Pos, guiContent, guiStyle);}
}

CustomGUILabel.cs​

  • ​功能​​:​​自定义文本标签控件​​。
  • ​核心特性​​:
    • 继承 CustomGUIControl,实现静态文本标签。
    • 支持带/不带样式的文本渲染(GUI.Label)。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;public class CustomGUIButton : CustomGUIControl
{public event UnityAction clickEvent;protected override void StyleOffDraw(){if(GUI .Button (guiPos .Pos ,guiContent )){clickEvent?.Invoke();}}protected override void StyleOnDraw(){if(GUI .Button (guiPos .Pos , guiContent,guiStyle )){clickEvent?.Invoke();}}
}

CustomGUIButton.cs​

  • ​功能​​:​​自定义按钮控件​​。
  • ​核心特性​​:
    • 继承 CustomGUIControl,实现按钮的绘制逻辑。
    • 支持带/不带样式的渲染 (StyleOnDraw/StyleOffDraw)。
    • 提供点击事件 clickEvent(通过 UnityAction 触发)。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;public class CustomGUIToggle : CustomGUIControl
{public bool isSel;public event UnityAction<bool> isSelEvent;private bool isOldSel;protected override void StyleOffDraw(){isSel =GUI.Toggle(guiPos.Pos, isSel, guiContent);//只有在变化时 才告诉外部 执行函数 否则 没必要一直告诉别人同一个值if(isOldSel != isSel){isSelEvent?.Invoke(isSel);isOldSel = isSel;}}protected override void StyleOnDraw(){isSel = GUI.Toggle(guiPos.Pos, isSel, guiContent,guiStyle);if (isOldSel != isSel){isSelEvent?.Invoke(isSel);isOldSel = isSel;}}
}

CustomGUIToggle.cs​

  • ​功能​​:​​自定义开关/复选框控件​​。
  • ​核心特性​​:
    • 继承 CustomGUIControl,实现开关逻辑。
    • 通过 isSel 存储状态,isSelEvent 事件通知状态变化。
    • 优化事件触发:仅在实际状态变化时调用事件。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;public class CustomGUIToggleGroup : MonoBehaviour
{public CustomGUIToggle[] customToggles;//用来记录判断的toggle上一次是不是trueprivate CustomGUIToggle frontToggle;// Start is called before the first frame updatevoid Start(){if (customToggles.Length == 0)return;//通过遍历为多个多选框添加监听事件函数//在函数中做处理//当一个为true是其他为falsefor (int i = 0; i < customToggles .Length; i++){CustomGUIToggle toggle = customToggles[i];toggle.isSelEvent += (value) =>{//当前传入的value是true时,需要将其他的改为falseif (value){//意味着其他的要改为falsefor (int j = 0; j < customToggles.Length; j++){//这里有闭包,toggle是上一个函数中声明的变量//改变了他的生命周期if(customToggles[j]!=toggle){customToggles[j].isSel = false;}}frontToggle = toggle;}//来判断 当前变成false的这个toggle是不是上一次是true//如果是的话,就不应该让他变成falseelse if(toggle== frontToggle){toggle.isSel = true;}};}}}

CustomGUIToggleGroup.cs​

  • ​功能​​:​​单选按钮组控制器​​。
  • ​核心特性​​:
    • 管理多个 CustomGUIToggle,实现单选逻辑。
    • 通过事件订阅确保组内只有一个开关被选中。
    • 防误操作:阻止已选开关被取消(frontToggle 机制)。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;public class CustomGUIInput : CustomGUIControl 
{public event UnityAction<string> textChange;private string oldStr = "";protected override void StyleOffDraw(){guiContent .text = GUI.TextField(guiPos.Pos, guiContent.text);if(oldStr !=guiContent .text){textChange?.Invoke(oldStr);oldStr = guiContent.text;}}protected override void StyleOnDraw(){if (oldStr != guiContent.text){guiContent.text = GUI.TextField(guiPos.Pos, guiContent.text,guiStyle);textChange?.Invoke(oldStr);oldStr = guiContent.text;}}}

CustomGUIInput.cs​

  • ​功能​​:​​自定义输入框控件​​。
  • ​核心特性​​:
    • 继承 CustomGUIControl,实现文本输入框逻辑。
    • 监听文本变化,通过 textChange 事件返回旧文本值。
    • 优化性能:仅在文本实际变化时触发事件。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;public enum E_Slider_Type
{Horizontal,Vertical,
}
public class CustomGUISlider : CustomGUIControl
{//最小值public float minValue;//最大值public float maxValue;//当前值public float nowValue;public GUIStyle thumbStyle;public E_Slider_Type slider_Type=E_Slider_Type.Horizontal;public event UnityAction<float> changeValue;private float oldValue;protected override void StyleOffDraw(){switch (slider_Type){case E_Slider_Type.Horizontal:nowValue = GUI.HorizontalSlider(guiPos.Pos, nowValue, minValue, maxValue);break;case E_Slider_Type.Vertical:nowValue = GUI.VerticalSlider(guiPos.Pos, nowValue, minValue, maxValue);break;}if(oldValue !=nowValue ){changeValue?.Invoke(oldValue);oldValue = nowValue;}}protected override void StyleOnDraw(){switch (slider_Type){case E_Slider_Type.Horizontal:nowValue = GUI.HorizontalSlider(guiPos.Pos, nowValue, minValue, maxValue,guiStyle ,thumbStyle );break;case E_Slider_Type.Vertical:nowValue = GUI.VerticalSlider(guiPos.Pos, nowValue, minValue, maxValue,guiStyle ,thumbStyle);break;}if (oldValue != nowValue){changeValue?.Invoke(oldValue);oldValue = nowValue;}}
}

CustomGUISlider.cs​

  • ​功能​​:​​自定义滑动条控件​​。
  • ​核心特性​​:
    • 支持水平/垂直滑动条(E_Slider_Type)。
    • 可配置最小值/最大值/当前值 (minValue/maxValue/nowValue)。
    • 提供滑块样式 (thumbStyle) 和值变化事件 changeValue
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class CustomGUITexture : CustomGUIControl
{public ScaleMode scaleMode = ScaleMode.StretchToFill;protected override void StyleOffDraw(){GUI.DrawTexture (guiPos.Pos,guiContent .image ,scaleMode);}protected override void StyleOnDraw(){GUI.DrawTexture(guiPos.Pos, guiContent.image, scaleMode);}
}

CustomGUITexture.cs​

  • ​功能​​:​​自定义纹理显示控件​​。
  • ​核心特性​​:
    • 继承 CustomGUIControl,实现纹理绘制。
    • 支持多种缩放模式 (ScaleMode),如拉伸填充 (StretchToFill)。
http://www.dtcms.com/a/316459.html

相关文章:

  • PHP‑ORT扩展构建纯PHP机器学习的推荐系统
  • Redis协议数据迁移方式
  • 聚焦智能穿戴“下一代消费终端”之争,Meta/微美全息借AI+AR积淀定义行业未来
  • Tasks and Deadlines(Sorting and Searching)
  • 【人工智能-18】机器学习:决策树、随机森林
  • 什么情况下浮动IP(Floating IP)会“漂移”(Drift)
  • 浮动IP(Floating IP)的删除通常需要满足什么条件
  • 小程序点击菜单栏实现样式动态切换
  • 对于包含大量文件的程序的便捷makefile操作
  • RK3568 Linux驱动学习——字符设备驱动开发
  • windows内核研究(软件调试-内存断点)
  • 永磁同步电机无速度算法--具有电流测量误差鲁棒性的永磁同步电机无传感器控制的自适应广义复矢量观测器
  • 二叉树算法
  • 02-算法
  • Java后端高频面试题
  • EP02:【DL 第二弹】张量的索引、分片、合并以及维度调整
  • 如何选择正确的体育/电竞数据接口服务商?
  • 力扣148:排序链表
  • Android 开发中,HandlerThread、IntentService 和 AsyncTask区别对比
  • Pytorch基础入门2
  • C++面向对象编程基础:从类定义到封装机制详解
  • 【Linux网络编程】socket基础
  • 风丘助力混合动力汽车工况测试:精准采集整车信号解决方案
  • Datawhale AI夏令营 第三期 task2 稍微改进
  • P1026 [NOIP 2001 提高组] 统计单词个数
  • 计算机网络:详解路由器如何转发子网数据包
  • Java JDBC连接池深度解析与实战指南
  • SAP PP CK466
  • 解决docker load加载tar镜像报json no such file or directory的错误
  • jQuery中Ajax返回字符串处理技巧