今天我们来全面讲一下Unity的UI之一IMGUI:
这是所有控件一个个使用案例的效果图(排列的满满登登,不浪费一丝空间)
接下来是代码部分,每部分都有详解,放到工程里可以直接用
using System;using System.Collections;using System.Collections.Generic;using Unity.VisualScripting;using UnityEngine;
public class IMGUITest : MonoBehaviour{ public Texture2D tex; public string textFieldString = "text field"; public string textAreaString = "text area"; public bool toggleBool = true;
private int toolbarInt = 0; private string[] toolbarStrings = { "b1", "b2", "b3" };
private int selectionGridInt = 0; private string[] selectionStrings = { "Grid 1", "Grid 2", "Grid 3", "Grid 4" }; private float hSliderValue = 0.0f; private float vSliderValue = 0.0f; private float hScrollbarValue; private float vScrollbarValue; private Vector2 scrollViewVector = Vector2.zero; private string innerText = "I am inside the ScrollView"; private Rect windowRect = new Rect(100, 130, 120, 120); private int selectedToolbar = 0; private string[] toolbarStringsA = { "One", "Two" };
public GUIStyle customButton; public GUISkin mySkin; private bool toggle = true;
// 256 x 32 的背景图像 public Texture2D bgImage;
// 256 x 32 的前景图像 public Texture2D fgImage; // 介于 0.0 和 1.0 之间的浮点数 public float playerEnergy = 1.0f;
private float sliderValue = 1.0f; private float maxSliderValue = 10.0f;
private float mySlider = 1.0f; private float staticMySlider = 1.0f; public Color myColor; void WindowFunction(int windowID) { // 在此处绘制窗口内的任何控件 if (GUI.Button(new Rect(0, 20, 80, 20), "WindowBtn")) { print("You clicked WindowBtn!"); } }
private void OnGUI() //每帧调用此函数 { //创建背景框(框的起点坐标,框的长宽)起点的原点为屏幕左上角,往左往下延伸长度, //GUI 控件的第二个参数是要与控件一起显示的实际内容,通常会希望在控件上显示一些文本或图像,不要用中文 //所有这些值都以__整数__提供,对应于像素值。所有 UnityGUI 控件均在__屏幕空间(Game视窗)中工作 此空间表示已发布的播放器的分辨率 GUI.Box(new Rect(130, 100, 80, 25), "IMGUIBox"); // 整个Button声明都位于if语句内,当游戏运行并单击Button时此if语句返回true,并执行if代码块中的所有代码。 if (GUI.Button(new Rect(0, 50, 100, 20), "Button1")) { Debug.Log("点击了按钮1"); } //声明控件的行与创建控件的行是同一行 if (Time.time % 2 < 1) //1秒执行一次 { if (GUI.Button(new Rect(0, 70, 100, 20), "flashing button")) { print("You clicked me!"); } } //可使用 Screen.width 和 Screen.height 属性来获取播放器中可用的屏幕空间的总尺寸 GUI.Box(new Rect(130, 70, 80,25), "Top-left"); GUI.Box(new Rect(Screen.width - 80, 0, 80, 25), "Top-right"); GUI.Box(new Rect(0, Screen.height - 25, 80, 25), "Bottom-left"); GUI.Box(new Rect(Screen.width - 80, Screen.height - 25, 80, 25), "Bottom-right");
GUI.Label(new Rect(0, 90, 100, 25), "Show Text");
//要显示图像,请声明 Texture2D 公共变量 GUI.Label(new Rect(0, 115, 100, 50), tex);
if (GUI.Button(new Rect(0, 165, 100, 50), tex)) { print("点击了图片按钮"); }
//此外还可通过第三个选项在 GUI 控件中一起显示图像和文本。为此, //可提供 GUIContent 对象作为 Content 参数,并定义要在 GUIContent 中显示的字符串和图像。 GUI.Box(new Rect(0, 215, 100, 50), new GUIContent("Tex", tex));
//此外,还可在 GUIContent 中定义__工具提示 (Tooltip)__,当鼠标悬停在 GUI 上时将工具提示显示在 GUI 中的其他位置。
// 此行将 "This is the tooltip" 传入 GUI.tooltip //tooltip只能字符串 GUI.Box(new Rect(0, 265, 100, 20), new GUIContent("hover me", "Box tooltip")); GUI.Button(new Rect(0, 285, 100, 20), new GUIContent("Click me", "Button tooltip")); // 此行读取并显示 GUI.tooltip 的内容 GUI.Label(new Rect(0, 305, 100, 20), GUI.tooltip); //也可以使用 GUIContent 来显示字符串、图标和工具提示。 GUI.Button(new Rect(0, 325, 100, 20), new GUIContent("Click me", tex, "This is the tooltip")); GUI.Label(new Rect(0, 345, 100, 20), GUI.tooltip);
//RepeatButton 是常规 Button 的变体。区别在于, //__RepeatButton__ 将响应鼠标按键保持按下状态的每一帧。由此可以创建单击并保持功能。 //在 UnityGUI 中,点击 RepeatButton 的每一帧都将返回 true if (GUI.RepeatButton(new Rect(0, 365, 100, 20), "RepeatButton")) { // RepeatButton 保持点击状态时的每一帧都将执行此代码 Debug.Log("鼠标持续按下中"); }
//TextField 控件是一个包含文本字符串的交互式可编辑单行字段。 //TextField 将始终显示一个字符串。必须提供要在 TextField 中显示的字符串。 //对字符串进行编辑时,TextField 函数将返回已编辑的字符串。 textFieldString = GUI.TextField(new Rect(0, 385, 100, 20), textFieldString);
//TextArea 控件是一个包含文本字符串的交互式可编辑多行区域。 //TextArea 将始终显示一个字符串。必须提供要在 TextArea 中显示的字符串。 //对字符串进行编辑时,TextArea 函数将返回已编辑的字符串。 textAreaString = GUI.TextArea(new Rect(0, 405, 100, 30), textAreaString);
//Toggle 控件创建具有持久开/关状态的复选框。用户可通过点击该复选框来更改状态。 //Toggle 开/关状态由 true/false 布尔值表示。必须提供布尔值作为参数来使 Toggle 表示实际状态。 //如果点击,则 Toggle 函数将返回一个新的布尔值。为了捕获此交互性,必须指定布尔值来接受 Toggle 函数的返回值。 toggleBool = GUI.Toggle(new Rect(0, 435, 100, 20), toggleBool, "Toggle"); if (toggleBool) { GUI.Label(new Rect(0, 455, 100, 20), "Open"); } else { GUI.Label(new Rect(0, 455, 100, 20), "Close"); } //Toolbar 控件本质上是一行 Button。在 Toolbar 上,一次只能有一个 Button 处于激活状态,并且此 Button 将一直保持激活状态,直到点击其他 Button。 //此行为模拟典型 Toolbar 的行为。在 Toolbar 上可以定义任意数量的 Button。 //oolbar 中处于激活状态的 Button 通过整数加以跟踪。必须在函数中提供整数作为参数。要使 Toolbar 具有交互性, //必须将整数分配给函数的返回值。提供的内容数组中的元素数将决定 Toolbar 中显示的 Button 数。
toolbarInt = GUI.Toolbar(new Rect(0, 475, 100, 20), toolbarInt, toolbarStrings);
//SelectionGrid 控件是一种多行 Toolbar。您可以决定网格中的列数和行数。一次只能激活一个 Button。 //SelectionGrid 中处于激活状态的 Button 通过整数加以跟踪。必须在函数中提供整数作为参数。 //要使 SelectionGrid 具有交互性,必须将整数分配给函数的返回值。 //提供的内容数组中的元素数将决定 SelectionGrid 中显示的 Button 数。还可以通过函数参数指定列数。
selectionGridInt = GUI.SelectionGrid(new Rect(0, 495, 100, 40), selectionGridInt, selectionStrings, 2);
//滑动条只需设置长度 //HorizontalSlider 控件是一个典型的水平滑钮,可拖动该滑钮来更改介于预定最小值和最大值之间的值。 //滑钮的位置存储为浮点数。要显示滑钮的位置,请将该浮点数作为函数中的参数之一。 //此外还有两个值用于确定最小值和最大值。如果希望滑钮可调,请将滑动条值浮点数指定为 Slider 函数的返回值。 hSliderValue = GUI.HorizontalSlider(new Rect(0, 540, 100, 0), hSliderValue, 0.0f, 10.0f);
//VerticalSlider 控件是一个典型的垂直滑钮,可拖动该滑钮来更改介于预定最小值和最大值之间的值。 //滑钮的位置存储为浮点数。要显示滑钮的位置,请将该浮点数作为函数中的参数之一。 //此外还有两个值用于确定最小值和最大值。如果希望滑钮可调,请将滑动条值浮点数指定为 Slider 函数的返回值。 vSliderValue = GUI.VerticalSlider(new Rect(120, 70, 0, 50), vSliderValue, 10.0f, 0.0f);
//HorizontalScrollbar 控件类似于 Slider 控件,但在视觉上类似于 Web 浏览器或文字处理程序的滚动元素。此控件用于导航 ScrollView 控件。 //Horizontal Scrollbar 的实现方式与 Horizontal Slider 相同,但有一个例外:还有一个参数用于控制滚动条滑钮本身的宽度。 hScrollbarValue = GUI.HorizontalScrollbar(new Rect(120, 50, 100, 0), hScrollbarValue, 0.1f, 0f, 10.0f); //倒数第三个值设置滑动条宽度
//VerticalScrollbar 控件类似于 Slider 控件,但在视觉上类似于 Web 浏览器或文字处理程序的滚动元素。此控件用于导航 ScrollView 控件。 //Vertical Scrollbar 的实现方式与 Vertical Slider 相同,但有一个例外:还有一个参数用于控制滚动条滑钮本身的高度。 vScrollbarValue = GUI.VerticalScrollbar(new Rect(100, 60, 0, 60), vScrollbarValue, 0.1f, 10.0f, 0.0f); //倒数第三个值设置滑动条宽度
//ScrollView 控件可显示一个包含更大控件集合的可视区域。 //ScrollView 需要两个 Rect 作为参数。第一个 Rect 定义 ScrollView 可视区域在屏幕上的位置和大小。第二个 Rect 定义可视区域内包含的空间大小。 //如果可视区域内的空间大于可视区域,则会根据需要显示滚动条。还必须分配并提供 2D 矢量,该矢量用于存储显示的可视区域的位置。 // 开始 ScrollView scrollViewVector = GUI.BeginScrollView(new Rect(230, 0, 100, 100), scrollViewVector, new Rect(0, 0, 400, 400)); // 在 ScrollView 中放入一些内容 innerText = GUI.TextArea(new Rect(0, 0, 400, 400), innerText); // 结束 ScrollView GUI.EndScrollView();
//Window 是可拖动的控件容器。点击时,Window 可获得和失去焦点。因此,实现方式与其他控件略有不同。 //每个 Window 都有一个 id 编号,并且其内容在一个单独的函数内声明,该函数在 Window 获得焦点时调用。 //Window 是唯一需要额外函数才能正常工作的控件。必须为 Window 提供 id 编号和要执行的函数名称。在 Window 函数中,可以创建实际行为或包含的控件。 windowRect = GUI.Window (0, windowRect, WindowFunction, "My Window");
//要检测用户是否在 GUI 中执行了任何操作(点击按钮、拖动滑动条等),应从脚本中读取 GUI.changed 值。当用户执行了操作时,结果将获得 true, //因此可以轻松验证用户输入。常见的情况是 Toolbar,这种情况下会希望根据 Toolbar 中已点击的 Button 来更改特定值。 //通常不希望在每次的调用 OnGUI() 中都分配该值,而只在点击其中一个 Button 时才分配该值。 // 确定哪个按钮处于激活状态,是否在此帧进行了点击 selectedToolbar = GUI.Toolbar(new Rect(100, 250, 120, 30), selectedToolbar, toolbarStringsA);
// 如果用户在此帧点击了新的工具栏按钮,我们将处理他们的输入 //如果用户操作了前面放置的任何 GUI 控件,__GUI.changed__ 将返回 true。好像RepeatButton没反应 if (GUI.changed) { Debug.Log("SomeOne was Touched");
if (0 == selectedToolbar) { Debug.Log("selectedToolbar was Touched"); } else { Debug.Log("other was Touched"); } }
//自定义IMGUI控件 //控件外观由 GUIStyle , 如果创建控件时未定义 GUIStyle,则会应用 Unity 的默认 GUIStyle //当有大量不同的 GUIStyle 可供使用时,可在单个 GUISkin 中定义这些样式。GUISkin 只不过是 GUIStyle 的集合。 //控件定义内容,而样式定义外观。通过这种机制可以创建外观像普通按钮但功能为开关的组合控件
//所有 GUI 控件函数都有可选的最后一个参数:用于显示控件的 GUIStyle。如果忽略此参数,则会使用 Unity 的默认 GUIStyle。 //函数内部会将控件类型的名称作为字符串应用,因此 GUI.Button() 使用“button”样式,__GUI.Toggle()__ 使用“toggle”样式, //等等。若要覆盖控件的默认 GUIStyle,可将其指定为最后一个参数。 // 创建使用 "box" GUIStyle 的标签。 GUI.Label(new Rect(100, 280, 120, 20), "looking like a box", "box");
// 创建使用 "toggle" GUIStyle 的按钮 GUI.Button(new Rect(100, 300, 120, 20), "This is a button", "toggle");
//创建公共变量 GUIStyle public GUIStyle customButton; //声明 GUIStyle 公共变量时,样式的所有元素都将显示在 Inspector 中。在该面板中可以编辑所有不同的值。 // 创建按钮。将上面定义的 GUIStyle 作为要使用的样式传入 //样式的修改是针对每个脚本和每个游戏对象进行的 GUI.Button(new Rect(100, 320, 120, 20), "I am a Custom Button", customButton);
//对于较复杂的 GUI 系统,将一系列样式集中保存在一个位置是很有意义的。这就是 GUISkin 的作用。GUISkin 包含多种不同的样式, //基本上能为所有 GUI 控件提供完整的外观修改。 //要使用已创建的皮肤,请将其分配给 OnGUI() 函数中的 GUI.skin。
GUI.skin = mySkin; // 创建按钮。此时将从分配给 mySkin 的皮肤获得默认的 "button" 样式。 GUI.Button(new Rect(100, 340, 120, 20), "Skinned Button");
// 创建开关。此时将从分配给 mySkin 的皮肤获得 "button" 样式。Toggle 打开按button的 OnNormal OnHover 等样式,关闭了 按button的 Normal Hover样式 toggle = GUI.Toggle(new Rect(100, 360, 120, 20), toggle, "Skinned Button", "button");
// 将当前皮肤指定为 Unity 的默认值。 GUI.skin = null;
// 创建按钮。此时将从内置皮肤获得默认的 "button" 样式。 GUI.Button(new Rect(100, 380, 120, 20), "Built-in Button");
//此示例将展示如何通过代码来动态更改字体大小。会影响到上面的Label字体大小 //将 GUIStyle 样式设置为标签 GUIStyle style = GUI.skin.GetStyle("label"); //将样式字体大小设置为随时间增大和减小 style.fontSize = (int)(20.0f + 10.0f * Mathf.Sin(Time.time));
//创建一个标签并使用当前设置来显示,字体不能平滑地改变大小,这是因为字体大小不是无限数量的。
//此特定示例要求加载默认字体(Arial) 并将其标记为动态。无法更改未标记为动态的任何字体的大小。 GUI.Label(new Rect(100, 400, 120, 80), "Hello World!");
//使用 IMGUI 系统时,可使用两种不同的模式来排列和组织 UI:固定布局模式和自动布局模式。 //到目前为止,本指南中提供的每个 IMGUI 示例都使用了固定布局。要使用自动布局,应在调用控件函数时写入 GUILayout 而不是 GUI。 //不必使用一种布局模式来替代另一种布局模式,可在同一 OnGUI() 函数中同时使用这两种模式。 //当有预先设计好的界面可供使用时,采用固定布局比较合理。如果预先不知道需要多少元素,或者不想费心进行每个控件的手动定位, //则采用自动布局比较合适。例如,如果要基于保存游戏文件创建大量不同的按钮,但无法准确知道要绘制多少按钮, //这种情况下采用自动布局可能会更加合理。具体实际上取决于游戏设计以及所需的界面呈现方式。使用自动布局时有两个主要的不同之处: //使用 GUILayout 而不是 GUI //自动布局控件不需要 Rect() 函数
// 固定布局 GUI.Button(new Rect(100, 480, 120, 20), "Fixed Layout Btn");
// 自动布局 根据字体长度扩宽按钮宽带 GUILayout.Button("I am an Automatic Layout Button");
//根据使用的布局模式,可通过不同的挂钩来控制控件的位置以及控件如何组合在一起。在固定布局中,可将不同的控件放入__组__中。 //在自动布局中,可将不同的控件放入__区域、水平组__和__垂直组__中 //固定布局 - 组, 组是固定布局模式中的布局规则。使用组可以定义包含多个控件的屏幕区域。为定义组中包含的控件, //需要使用 GUI.BeginGroup() 和 GUI.EndGroup() 函数。 //组内的所有控件将根据组的左上角而不是屏幕的左上角进行定位。因此,如果在运行时重新定位组,则将保持组中所有控件的相对位置。
// 在屏幕中央创建一个组 GUI.BeginGroup(new Rect(100, 500, 100, 100)); // 现在所有矩形都调整到该组。(0,0) 是该组的左上角。
//我们将创建一个框形,以便能看到该组在屏幕上的位置。 GUI.Box(new Rect(0, 0, 100, 100), "Group is here"); GUI.Button(new Rect(10, 40, 80, 30), "Click me");
// 结束我们上面开始的组。记住这一点非常重要! GUI.EndGroup();
//还可以将多个组嵌套在一起。这样做时,每个组的内容都会裁剪到其父项空间。 // 创建一个组来包含这两个图像 // 调整前 2 个坐标以将其放在屏幕上的其他位置 GUI.BeginGroup(new Rect(100, 600, 120, 30)); // 绘制背景图像 GUI.Box(new Rect(0, 0, 120, 30), bgImage); // 创建将被裁剪的第二个组 // 我们想要裁剪图像而不是缩放图像,这就是我们需要第二个组的原因 GUI.BeginGroup(new Rect(0, 0, playerEnergy * 120, 30)); // 绘制前景图像 GUI.Box(new Rect(0, 0, 120, 30), fgImage); // 结束这两个组 GUI.EndGroup(); GUI.EndGroup();
//区域仅用于自动布局模式。区域定义了有限的屏幕区域来包含 GUILayout 控件,因此在功能上类似于固定布局组。 //由于自动布局的性质,几乎始终要用到区域。在自动布局模式下,不需要在控制级别定义绘制控件的屏幕区域。 //控件将自动放置在包含该控件的区域的最左上角。此区域可能是指屏幕。此外也可以创建手动定位的区域。 //一个区域内的 GUILayout 控件将放置在该区域的最左上角。
GUILayout.Button("I am not inside an Area"); GUILayout.BeginArea(new Rect(100,640 ,120, 30)); GUILayout.Button("inside an Area"); GUILayout.EndArea();
//请注意,在一个区域内,具有可见元素(如按钮和框形)的控件会将宽度拉伸到该区域的整个长度。
//自动布局 - 水平和垂直组 //使用自动布局时,默认情况下控件将从上到下依次出现。在很多情况下,需要更精确控制控件的放置位置以及排列方式。 //如果使用自动布局模式,则可以选择水平和垂直组。与其他布局控件一样,可以调用单独的函数来开始或结束这些组。 //这些函数为 GUILayout.BeginHorizontal()、GUILayout.EndHorizontal()、GUILayout.BeginVertical() 和 GUILayout.EndVertical()。 //水平组内的所有控件都将始终采用水平布局方式。垂直组内的所有控件都将始终采用垂直布局方式。这听起来很简单, //但若要将组嵌套在彼此内部,就不那么简单了。通过嵌套的方式可在任何能够想象的配置中排列任意数量的控件。 // 将所有控件包裹在指定的 GUI 区域中 GUILayout.BeginArea(new Rect(220, 100, 200, 60)); // 开始单个水平组 GUILayout.BeginHorizontal(); // 正常放置按钮 if (GUILayout.RepeatButton("Increase max\nSlider Value")) { maxSliderValue += 3.0f * Time.deltaTime; } // 在按钮旁边垂直排列另外两个控件 GUILayout.BeginVertical(); GUILayout.Box("Slider Value: " + Mathf.Round(sliderValue)); sliderValue = GUILayout.HorizontalSlider(sliderValue, 0.0f, maxSliderValue); // 结束这些组和区域 GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.EndArea();
//可使用 GUILayoutOption 覆盖某些自动布局参数。要执行此操作,可提供相应的选项作为 GUILayout 控件的最终参数。 //在上面的区域示例中,是否还记得按钮的宽度扩展到区域宽度的 100 %?如果愿意,我们可以覆盖这种行为。 GUILayout.BeginArea(new Rect(330, 0, Screen.width - 410, 50)); GUILayout.Button("I am a regular Automatic Layout Button"); GUILayout.Button("My width has been overridden", GUILayout.Width(300)); GUILayout.EndArea();
style.fontSize = 15; //让字体动画不再影响后续字体
//复合控件 //可将每个 GUI.Label() 调用与 GUI.HorizontalSlider() 调用进行搭配, //或者可创建一个同时包含 Label 和 Slider 的__复合控件__。 mySlider = LabelSlider(new Rect(220, 140, 200, 20), mySlider, 5.0f, "Label text here");
//静态复合控件 //通过使用__静态__函数,可以创建自成一体的完整复合控件集合。这样,就不必在需要使用函数的同一脚本中声明该函数 staticMySlider = IMGUITest.StaticLabelSlider(new Rect(220, 170, 200, 20), staticMySlider, 5.0f, "Static Label text here");
//精心设计的复合控件 //可使用复合控件实现出色的创造性。可按任何喜欢的方式对复合控件进行排列和分组。以下示例将创建可重复使用的 RGB Slider。 myColor = RGBSlider(new Rect(220, 220, 200, 20), myColor);
//我们构建一些位于彼此之上的复合控件,从而演示如何在复合控件中使用其他复合控件。 //为此,我们将创建如上所示的新 RGB Slider,但我们将使用 LabelSlider 来执行此操作。 //这样我们将始终有一个标签显示哪个滑动条对应哪种颜色。 myColor = RGBSlider2(new Rect(220, 280, 200, 20), myColor);
} float LabelSlider(Rect screenRect, float sliderValue, float sliderMaxValue, string labelText) { GUI.Label(screenRect, labelText); // <- 将 Slider 推到 Label 的末尾 //screenRect.x += screenRect.width; screenRect.y += 20; sliderValue = GUI.HorizontalSlider(screenRect, sliderValue, 0.0f, sliderMaxValue); return sliderValue; }
public static float StaticLabelSlider(Rect screenRect, float sliderValue, float sliderMaxValue, string labelText) { GUI.Label(screenRect, labelText);
// <- 将 Slider 推到 Label 的末尾 //screenRect.x += screenRect.width; screenRect.y += 20;
sliderValue = GUI.HorizontalSlider(screenRect, sliderValue, 0.0f, sliderMaxValue); return sliderValue; } Color RGBSlider(Rect screenRect, Color rgb) { rgb.r = GUI.HorizontalSlider(screenRect, rgb.r, 0.0f, 1.0f);
// <- 将下一个控件向下移动一点以避免重叠 screenRect.y += 20; rgb.g = GUI.HorizontalSlider(screenRect, rgb.g, 0.0f, 1.0f);
// <- 将下一个控件向下移动一点以避免重叠 screenRect.y += 20;
rgb.b = GUI.HorizontalSlider(screenRect, rgb.b, 0.0f, 1.0f); return rgb; } Color RGBSlider2(Rect screenRect, Color rgb) { rgb.r = LabelSlider(screenRect, rgb.r, 1.0f, "Red");
// <- 将下一个控件向下移动一点以避免重叠 screenRect.y += 30; rgb.g = LabelSlider(screenRect, rgb.g, 1.0f, "Green");
// <- 将下一个控件向下移动一点以避免重叠 screenRect.y += 30;
rgb.b = LabelSlider(screenRect, rgb.b, 1.0f, "Blue");
return rgb; }}
Unity的UI有好多种,IMGUI , UGUI ,UITookit , UIWidget, 插件部分有NGUI , FGUI 等等。学不完,根本学不完,慢慢学!
领取专属 10元无门槛券
私享最新 技术干货