Unity的IMGUI

简介

IMGUI是一个代码驱动的GUI系统。由对实现它的任何脚本上的OnGUI函数的调用进行驱动。

IMGUI通常用于:

  • 创建游戏内调试显示和工具。
  • 创建脚本组件自定义的Inspectors。
  • 创建editor窗口和工具来扩展Unity本身。

基础

OnGUI()代码每帧都会被调用,因此无需显式创建或销毁GUI控件。

声明GUI控件的三个要素为:控件类型,位置和内容。

控件类型 Type( 位置 Rect, 内容 String/GUIContent );

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour
{
void OnGUI ()
{
if (Time.time % 2 < 1)
{
if (GUI.Button (new Rect (10,10,200,20), "Meet the flashing button"))
{
print ("You clicked me!");
}
}
}
}

控件类型

控件类型通过调用Unity的GUI类或GUILayout类中的函数来声明。

GUI.Label

标签控件GUI.Label是非交互式的。它只用于显示无法单击或拖动。最适合只显示信息。

1
2
3
4
5
6
7
8
9
10
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour
{
void OnGUI ()
{
GUI.Label (new Rect (25, 25, 100, 30), "Label");
}
}

GUI.Button

按钮控件GUI.Button是典型的交互式按钮。鼠标按下时,无论鼠标保持按下多久都只会响应一次。释放鼠标按钮后,将立即发生响应。

鼠标单击时返回true,否则为false

1
2
3
4
5
6
7
8
9
10
11
12
13
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour
{
void OnGUI ()
{
if (GUI.Button (new Rect (25, 25, 100, 30), "Button"))
{
// This code is executed when the Button is clicked
}
}
}

GUI.RepeatButton

重复按钮控件GUI.RepeatButton是常规按钮的变体。GUI.RepeatButton将响应鼠标按钮保持按下的每一帧。这允许你创建单击并按住功能。

鼠标按住时每一帧都返回true

1
2
3
4
5
6
7
8
9
10
11
12
13
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour
{
void OnGUI ()
{
if (GUI.RepeatButton (new Rect (25, 25, 100, 30), "RepeatButton"))
{
// This code is executed every frame that the RepeatButton remains clicked
}
}
}

GUI.TextField

文本输入框控件GUI.TextField是一个可编辑单行文本字段。

文本字段将始终显示一个字符串。必须提供要在文本字段中显示的字符串。对字符串进行编辑时,TextField函数将返回编辑后的字符串。

1
2
3
4
5
6
7
8
9
10
11
12
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour
{
private string textFieldString = "text field";

void OnGUI ()
{
textFieldString = GUI.TextField (new Rect (25, 25, 100, 30), textFieldString);
}
}

GUI.TextArea

文本输入域控件GUI.TextArea是一个可编辑多行文本区域。

文本字段将始终显示一个字符串。必须提供要在文本字段中显示的字符串。对字符串进行编辑时,TextField函数将返回编辑后的字符串。

1
2
3
4
5
6
7
8
9
10
11
12
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour
{
private string textAreaString = "text area";

void OnGUI ()
{
textAreaString = GUI.TextArea (new Rect (25, 25, 100, 30), textAreaString);
}
}

GUI.Toggle

选择框控件GUI.Toggle会创建一个保持开/关的选项框。用户可以通过单击来改变状态。

true为选中。

1
2
3
4
5
6
7
8
9
10
11
12
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour
{
private bool toggleBool = true;

void OnGUI ()
{
toggleBool = GUI.Toggle (new Rect (25, 25, 100, 30), toggleBool, "Toggle");
}
}

GUI.Toolbar

工具条控件GUI.Toolbar本质上是一组只能有单个被选中的按钮。

1
2
3
4
5
6
7
8
9
10
11
12
13
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour
{
private int toolbarInt = 0;
private string[] toolbarStrings = {"Toolbar1", "Toolbar2", "Toolbar3"};

void OnGUI ()
{
toolbarInt = GUI.Toolbar (new Rect (25, 25, 250, 30), toolbarInt, toolbarStrings);
}
}

GUI.SelectionGrid

选择网格控件GUI.SelectionGrid是一个多行的GUI.Toolbar。可以确定网格中的列和行。同一组中只能有个一个按钮被选中。

1
2
3
4
5
6
7
8
9
10
11
12
13
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour
{
private int selectionGridInt = 0;
private string[] selectionStrings = {"Grid 1", "Grid 2", "Grid 3", "Grid 4"};

void OnGUI ()
{
selectionGridInt = GUI.SelectionGrid (new Rect (25, 25, 300, 60), selectionGridInt, selectionStrings, 2);
}
}

GUI.HorizontalSlider

水平滑块控件GUI.HorizontalSlider是一个典型的水平滑动按钮,可以拖动它在预定的最小值和最大值之间更改值。

1
2
3
4
5
6
7
8
9
10
11
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour
{
private float hSliderValue = 0.0f;
void OnGUI ()
{
hSliderValue = GUI.HorizontalSlider (new Rect (25, 25, 100, 30), hSliderValue, 0.0f, 10.0f);
}
}

GUI.VerticalSlider

垂直滑块控件GUI.VerticalSlider是一个典型的垂直滑动按钮,可以拖动它在预定的最小值和最大值之间更改值。

1
2
3
4
5
6
7
8
public class GUITest : MonoBehaviour 
{
private float vSliderValue = 0.0f;
void OnGUI ()
{
vSliderValue = GUI.VerticalSlider (new Rect (25, 25, 100, 30), vSliderValue, 10.0f, 0.0f);
}
}

GUI.HorizontalScrollbar

水平滚动条控件GUI.HorizontalScrollbar类似于滑块控件。被用于导航GUI.BeginScrollView/GUI.EndScrollView滚动视图控件。

1
2
3
4
5
6
7
8
9
10
11
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour
{
private float hScrollbarValue;
void OnGUI ()
{
hScrollbarValue = GUI.HorizontalScrollbar (new Rect (25, 25, 100, 30), hScrollbarValue, 1.0f, 0.0f, 10.0f);
}
}

GUI.VerticalScrollbar

垂直滚动条控件GUI.VerticalScrollbar类似于滑块控件。被用于导航GUI.BeginScrollView/GUI.EndScrollView滚动视图控件。

1
2
3
4
5
6
7
8
9
10
11
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour
{
private float vScrollbarValue;
void OnGUI ()
{
vScrollbarValue = GUI. VerticalScrollbar (new Rect (25, 25, 100, 30), vScrollbarValue, 1.0f, 10.0f, 0.0f);
}
}

GUI.BeginScrollView/GUI.EndScrollView

滚动视图GUI.BeginScrollView/GUI.EndScrollView是将范围内的控件加入到一个带有滚动条的视图。

ScrollViews需要两个Rects作为参数。第一个Rect定义屏幕上可以查看的滚动视图的位置和大小。第二个Rect定义可视区域内包含的空间大小。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour
{
private Vector2 scrollViewVector = Vector2.zero;
private string innerText = "I am inside the ScrollView";

void OnGUI ()
{
// Begin the ScrollView
scrollViewVector = GUI.BeginScrollView (new Rect (25, 25, 100, 100), scrollViewVector, new Rect (0, 0, 400, 400));

// Put something inside the ScrollView
innerText = GUI.TextArea (new Rect (0, 0, 400, 400), innerText);

// End the ScrollView
GUI.EndScrollView();
}
}

GUI.Window

窗口控件Window是可拖拽的容器。单击时,他们会接收和失去焦点。每个窗口控件都有一个ID,其内容在窗口控件具有焦点时调用的单独函数中声明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour
{
private Rect windowRect = new Rect (20, 20, 120, 50);

void OnGUI ()
{
windowRect = GUI.Window (0, windowRect, WindowFunction, "My Window");
}

void WindowFunction (int windowID)
{
// Draw any Controls inside the window here
}
}

GUI.changed

要检测用户是否在GUI中执行了任何操作(例如:单击按钮,拖动滑块),请从脚本中读取GUI.changed的值。执行了操作,将返回true

一种常见的方案是工具栏,希望根据单击工具栏中的按钮来改变特定的值。但不想每次调用OnGUI()时分配值,而是在单击其中一个按钮时才分配值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour
{

private int selectedToolbar = 0;
private string[] toolbarStrings = {"One", "Two"};

void OnGUI ()
{
// Determine which button is active, whether it was clicked this frame or not
selectedToolbar = GUI.Toolbar (new Rect (50, 10, Screen.width - 100, 30), selectedToolbar, toolbarStrings);

// If the user clicked a new Toolbar button this frame, we'll process their input
if (GUI.changed)
{
Debug.Log("The toolbar was clicked");

if (0 == selectedToolbar)
{
Debug.Log("First button was clicked");
}
else
{
Debug.Log("Second button was clicked");
}
}
}
}

控件位置

Position是任何GUI控制函数的第一个参数。该参数是一个Rect类型。

可以通过获取屏幕宽度Screen.width和高度Screen.height来控制控件的相对位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour
{

void OnGUI()
{
GUI.Box (new Rect (0,0,100,50), "Top-left");
GUI.Box (new Rect (Screen.width - 100,0,100,50), "Top-right");
GUI.Box (new Rect (0,Screen.height - 50,100,50), "Bottom-left");
GUI.Box (new Rect (Screen.width - 100,Screen.height - 50,100,50), "Bottom-right");
}

}

控件内容

GUI控件的第二个参数用于控制控件显示的内容。可以显示文本或图像。

如果需要同时显示图像和文本。可以使用GUIContent类型作为参数,并定义GUIContent的文字和图像。

还可以通过GUIContent定义tooltip,并在鼠标悬停时显示提示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour
{
public Texture2D controlTexture;

public Texture2D icon;

void OnGUI ()
{
//显示文本
GUI.Label(new Rect(10, 0, 100, 50), "This is the text string for a Label Control");

//显示图片
GUI.Label(new Rect(10, 70, 100, 50), controlTexture);

//按钮显示文字
if(GUI.Button(new Rect(10, 130, 100, 20), "This is text"))
{
print("you clicked the text button");
}

//按钮显示图片
if(GUI.Button(new Rect(10, 160, 100, 50), icon))
{
print("you clicked the icon");
}

//方形区域通过GUIContent显示文字和图片
GUI.Box(new Rect(10, 220, 100, 50), new GUIContent("This is text", icon));

//按钮通过GUIContent显示文字和悬浮提示
GUI.Button(new Rect(10, 280, 100, 20), new GUIContent("Click me1", "This is the tooltip1"));

//定义悬浮提示的位置和大小
GUI.Label(new Rect(10, 310, 200, 20), GUI.tooltip);

//按钮通过GUIContent显示文字,图片和悬浮提示
GUI.Button(new Rect(10, 340, 100, 20), new GUIContent("Click me2", icon, "This is the tooltip2"));

//定义悬浮提示的位置和大小
GUI.Label(new Rect(10, 380, 200, 20), GUI.tooltip);
}
}

自定义控件

自定义外观

在未自定义GUIStyles的情况下,会使用Unity默认的GUIStyles

GUIStyles

所有GUI控件的函数的最后一个可选参数:用于选择控件的GUIStyles。省略此项,使用Unity默认GUIStyles

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour
{
void OnGUI ()
{
// Make a label that uses the "box" GUIStyle.
GUI.Label (new Rect (0,0,200,100), "Hi - I'm a label looking like a box", "box");

// Make a button that uses the "toggle" GUIStyle
GUI.Button (new Rect (10,140,180,20), "This is a button", "toggle");
}
}

声明公共变量的GUIStyle

声明公共变量的GUIStyle时,可以在Inspector中查看和配置GUIStyle

1
2
3
4
5
6
7
8
9
10
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour {
public GUIStyle customButton;
void OnGUI () {
// Make a button. We pass in the GUIStyle defined above as the style to use
GUI.Button (new Rect (10,10,150,20), "I am a Custom Button", customButton);
}
}

动态更改GUIStyle属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using UnityEngine;
using System.Collections;

public class Fontsize : MonoBehaviour
{
void OnGUI ()
{
//Set the GUIStyle style to be label
GUIStyle style = GUI.skin.GetStyle ("label");

//Set the style font size to increase and decrease over time
style.fontSize = (int)(20.0f + 10.0f * Mathf.Sin (Time.time));

//Create a label and display with the current settings
GUI.Label (new Rect (10, 10, 200, 80), "Hello World!");
}
}

GUISkins

GUISkinsGUIStyles的集合。GUIStyles定义GUI控件的外观。如果使用GUIStyles,则不必使用GUISkins

当有大量不同的GUIStyles需要使用时,可以在单个GUISkins中定义他们。

创建GUISkins

Assets->Create->GUI Skin

应用GUISkins

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour {

public GUISkin mySkin;
void OnGUI () {
// Assign the skin to be the one currently used.
GUI.skin = mySkin;

// Make a button. This will get the default "button" style from the skin assigned to mySkin.
GUI.Button (new Rect (10,10,150,20), "Skinned Button");
}
}

布局模式

分为Fixed布局和Automatic布局。

OnGUI()中可以同时使用两种布局模式。

界面的元素数量和位置确定的时候使用Fixed,反之使用Automatic模式。

使用Automatic模式的主要区别:

  • 使用GUILayout代替GUI
  • 使用Automatic布局控件不需要Rect()函数。
1
2
3
4
5
6
7
8
9
10
11
12
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour {
void OnGUI () {
// Fixed Layout
GUI.Button (new Rect (25,25,100,30), "I am a Fixed Layout Button");

// Automatic Layout
GUILayout.Button ("I am an Automatic Layout Button");
}
}

排列控件

分为: Fixed布局-GroupsAutomatic布局-AreasAutomatic布局-HorizontalAutomatic布局-Vertical

获取当前窗口大小

当前窗口宽度:Screen.width

当前窗口高度:Screen.height

Fixed布局-Groups

使用GUI.BeginGroup()和GUI.EndGroup()函数。组内控件是根据组的左上角,而不是屏幕的左上角进行定位。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour {
void OnGUI () {
// Make a group on the center of the screen
GUI.BeginGroup (new Rect (Screen.width / 2 - 50, Screen.height / 2 - 50, 100, 100));
// All rectangles are now adjusted to the group. (0,0) is the topleft corner of the group.

// We'll make a box so you can see where the group is on-screen.
GUI.Box (new Rect (0,0,100,100), "Group is here");
GUI.Button (new Rect (10,40,80,30), "Click me");

// End the group we started above. This is very important to remember!
GUI.EndGroup ();
}
}

可以使用多个组互相嵌套。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour {
// background image that is 256 x 32
public Texture2D bgImage;
// foreground image that is 256 x 32
public Texture2D fgImage;
// a float between 0.0 and 1.0
public float playerEnergy = 1.0f;

void OnGUI () {
// Create one Group to contain both images
// Adjust the first 2 coordinates to place it somewhere else on-screen
GUI.BeginGroup (new Rect (0,0,256,32));

// Draw the background image
GUI.Box (new Rect (0,0,256,32), bgImage);

// Create a second Group which will be clipped
// We want to clip the image and not scale it, which is why we need the second Group
GUI.BeginGroup (new Rect (0,0,playerEnergy * 256, 32));

// Draw the foreground image
GUI.Box (new Rect (0,0,256,32), fgImage);

// End both Groups
GUI.EndGroup ();

GUI.EndGroup ();
}
}

Automatic布局-Areas

Automatic的默认模式。

默认是类似于FixedGroups布局。

以左上角为锚点。

区域内具有可见元素的控件宽度会拉整个区域的宽度。

使用GUILayout.BeginArea()GUILayout.EndArea()来创建区域。

1
2
3
4
5
6
7
8
9
10
11
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour {
void OnGUI () {
GUILayout.Button ("I am not inside an Area");
GUILayout.BeginArea (new Rect (Screen.width/2, Screen.height/2, 300, 300));
GUILayout.Button ("I am completely inside an Area");
GUILayout.EndArea ();
}
}

Automatic布局-Horizontal和Vertical

通过使用GUILayout.BeginHorizontal()GUILayout.EndHorizontal()GUILayout.BeginVertical()GUILayout.EndVertical()函数,决定区域内的组件的排列放松。可以嵌套。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour {
private float sliderValue = 1.0f;
private float maxSliderValue = 10.0f;

void OnGUI()
{
// Wrap everything in the designated GUI Area
GUILayout.BeginArea (new Rect (0,0,200,60));

// Begin the singular Horizontal Group
GUILayout.BeginHorizontal();

// Place a Button normally
if (GUILayout.RepeatButton ("Increase max\nSlider Value"))
{
maxSliderValue += 3.0f * Time.deltaTime;
}

// Arrange two more Controls vertically beside the Button
GUILayout.BeginVertical();
GUILayout.Box("Slider Value: " + Mathf.Round(sliderValue));
sliderValue = GUILayout.HorizontalSlider (sliderValue, 0.0f, maxSliderValue);

// End the Groups and Area
GUILayout.EndVertical();
GUILayout.EndHorizontal();
GUILayout.EndArea();
}
}

GUILayoutOptions定义控件

可以使用GUILayoutOptions覆盖某些自动布局参数。

1
2
3
4
5
6
7
8
9
10
11
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour {
void OnGUI () {
GUILayout.BeginArea (new Rect (100, 50, Screen.width-200, Screen.height-100));
GUILayout.Button ("I am a regular Automatic Layout Button");
GUILayout.Button ("My width has been overridden", GUILayout.Width (95));
GUILayout.EndArea ();
}
}

扩展IMGUI

复合控件

在GUI中可能出现两种类型的控件同时出现的情况。就可以创建同时包含多种控件的复合控件。

例如:该控件同时包含LabelHorizontalSlider

在此示例中,LabelSlider()会返回正确的值,以使其具有交互性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour {
private float mySlider = 1.0f;
void OnGUI () {
mySlider = LabelSlider (new Rect (10, 100, 100, 20), mySlider, 5.0f, "Label text here");
}

float LabelSlider (Rect screenRect, float sliderValue, float sliderMaxValue, string labelText) {
GUI.Label (screenRect, labelText);

// <- Push the Slider to the end of the Label
screenRect.x += screenRect.width;

sliderValue = GUI.HorizontalSlider (screenRect, sliderValue, 0.0f, sliderMaxValue);
return sliderValue;
}
}

静态复合控件

通过使用静态函数,可以创建自己的复合控件集合。这样能使控件复用。

这样其他脚本需要使用LabelSlider就只需要调用CompoundControls.LabelSlider(),传入正确的参数和处理返回值就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using UnityEngine;
using System.Collections;

public class CompoundControls : MonoBehaviour {
public static float LabelSlider (Rect screenRect, float sliderValue, float sliderMaxValue, string labelText) {
GUI.Label (screenRect, labelText);

// <- Push the Slider to the end of the Label
screenRect.x += screenRect.width;

sliderValue = GUI.HorizontalSlider (screenRect, sliderValue, 0.0f, sliderMaxValue);
return sliderValue;
}
}

可复用的RGB滑块示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour {
public Color myColor;
void OnGUI () {
myColor = RGBSlider (new Rect (10,10,200,10), myColor);
}

Color RGBSlider (Rect screenRect, Color rgb) {
rgb.r = GUI.HorizontalSlider (screenRect, rgb.r, 0.0f, 1.0f);

// <- Move the next control down a bit to avoid overlapping
screenRect.y += 20;
rgb.g = GUI.HorizontalSlider (screenRect, rgb.g, 0.0f, 1.0f);

// <- Move the next control down a bit to avoid overlapping
screenRect.y += 20;

rgb.b = GUI.HorizontalSlider (screenRect, rgb.b, 0.0f, 1.0f);
return rgb;
}
}

现在在其他复合组件的基础上来构建复合组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour {
public Color myColor;
void OnGUI () {
myColor = RGBSlider (new Rect (10,10,200,30), myColor);
}

Color RGBSlider (Rect screenRect, Color rgb) {
rgb.r = CompoundControls.LabelSlider (screenRect, rgb.r, 1.0f, "Red");

// <- Move the next control down a bit to avoid overlapping
screenRect.y += 20;
rgb.g = CompoundControls.LabelSlider (screenRect, rgb.g, 1.0f, "Green");

// <- Move the next control down a bit to avoid overlapping
screenRect.y += 20;

rgb.b = CompoundControls.LabelSlider (screenRect, rgb.b, 1.0f, "Blue");

return rgb;
}
}