个人博客:http://www.chenjianqu.com/
原文链接:http://www.chenjianqu.com/show-6.html
GUI是Graphical User Interface的缩写。Unity的图形界面系统能容易和快速创建出各种交互界面。游戏界面是游戏作品中不可或缺的部分,它可以为游戏提供导航,也可以为游戏内容提供重要的信息,同时也是美化游戏的一个重要手段。Unity内置了一套完整地GUI系统,提供了从布局、控件到皮肤的一整套GUI解决方案,可以做出各种风格和样式的GUI界面。目前Unity没有提供内置的GUI可视化编辑器,因此GUI界面的制作需要全部通过编写脚本代码来实现。
GUI的代码需要写在OnGUI()这个函数里,每当重绘GUI的时候,都会执行这个方法。每一帧都会重绘GUI。
首先在Unity里面新建一个C#脚本,把它附加到任意一个游戏实体上。然后在脚本里面创建一个OnGUI()函数。如下图所示,我们打算创建一个Button控件。
创建函数返回的是一个bool值,当用户点击按钮时返回True,否则为False。创建大多数GUI控件都需要一个Rect对象用于设置控件的大小和位置。对于Button控件来说,第二个参数可以为一个字符串,也可以为图像。如下图:
运行效果:
对于GUI来说,屏幕的左上角为坐标原点,右下角的坐标为(Screen.Width,Screen.Height)。Rect对象的四个参数分别代表:控件左上角x坐标,控件左上角y坐标,控件的宽度,控件的高度。
常见的GUI控件:
Lable:绘制文本和图片
Box:绘制一个图形框
Button:绘制一个响应单击事件的按钮
RepeatButton:绘制一个处理连续按下事件的按钮
TextField:绘制一个单行文本输入框
PasswordField:绘制一个密码输入框
TextArea:绘制一个多行文本输入框
Togle:绘制一个开关
Toolbar:绘制一个工具条
SelectionGrid:绘制一组网格按钮
HorizontalSlider:绘制一个水平方向的滑动条
VerticalSlider:绘制一个垂直方向的滑动条
HorizontalScrollbar:绘制一个水平方向的滚动条
VerticalScrollbar:绘制一个垂直方向的滚动条
Window:绘制一个窗口,可以用于放置控件。
有个需要注意的地方,比如我代码这样写:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GUILayout_Test : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
private void OnGUI()
{
if(GUI.RepeatButton(new Rect(10, 10, 100, 20), "你好"))
{
print("OK");
}
GUI.TextArea(new Rect(10, 30, 100, 20),"ABC");
}
运行的结果如下:
从图中可知,当我长按“你好”按钮的时候,输出窗口会输出“OK”,也就是说,print(“OK”)被反复执行,这就是RepeatButton和Button的不同之处。
还有就是这个代码是无法对文本框里的ABC进行编辑的,要想编辑文本框正确的写法如下:
private string str = "ABC";
private void OnGUI()
{
if(GUI.RepeatButton(new Rect(10, 10, 100, 20), "你好"))
{
print("OK");
}
str=GUI.TextArea(new Rect(10, 30, 100, 20),str);
}
其他需要改变内容的控件做法类似。
简单例子——绘制登录窗口:
private string userName = "";
private string password = "";
private bool manSex = false;
private bool womanSex = false;
private void OnGUI()
{
GUI.Label(new Rect(10, 10, 70, 20), "用户名:");
userName = GUI.TextArea(new Rect(80, 10, 200, 20), userName);
GUI.Label(new Rect(10, 30, 70, 20), "密码:");
password = GUI.PasswordField(new Rect(80, 30, 200, 20),password,'*');
manSex = GUI.Toggle(new Rect(10, 70, 50, 20), manSex, "男");
womanSex = GUI.Toggle(new Rect(10, 120, 50, 20), womanSex, "女");
GUI.Button(new Rect(10, 170, 50, 20), "OK");
}
说明:PasswordField的第三个参数是掩盖密码的字符。
有时候我们想更好的管理和排布控件,这个时候我们需要用到群组(Group)。
群组 (Groups) 是固定布局模式 (Fixed Layout Mode) 下可用的惯例,使您能够定义包含多个控件 (Controls) 的屏幕区域。您通过使用GUI.BeginGroup() 函数和GUI.EndGroup() 函数来定义哪些控件 (Controls) 在一个群组 (Group) 内。一个群组 (Group) 内的所有控件 (Controls) 都将基于群组 (Group) 的左上角来放置而不是按屏幕的左上角。以此方式,当您在运行时重新调整该群组的位置时,群组内所有控件 (Controls) 的相对位置保持不变。
例如,可以很容易地把多个控件 (Controls) 放置在屏幕中心:
void OnGUI () {
// 在屏幕中心创建群组
GUI.BeginGroup (new Rect (Screen.width / 2 - 50, Screen.height / 2 - 50, 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 ();
}
也可以将多个群组 (Group) 嵌套进彼此内部。进行嵌套后,每个群组的内容都被剪切到其父级空间:
using UnityEngine;
using System.Collections;
public class GUITest : MonoBehaviour {
// 256 x 32 背景图像
public Texture2D bgImage;
// 256 x 32 前景图像
public Texture2D fgImage;
// 0.0 和 1.0 之间的一个浮点数
public float playerEnergy = 1.0f;
void OnGUI () {
// 创建群组 (Group) 来容纳两幅图像
// 调整开始的 2 个坐标以便将其放置在屏幕上的其他某处
GUI.BeginGroup (new Rect (0,0,256,32));
// 绘制背景图像
GUI.Box (new Rect (0,0,256,32), bgImage);
// 创建将被剪切的第二群组 (Group)
// 我们要剪切而不是缩放图像,所以需要第二群组 (Group)
GUI.BeginGroup (new Rect (0,0,playerEnergy * 256, 32));
// 绘制前景图像
GUI.Box (new Rect (0,0,256,32), fgImage);
// 结束两个群组 (Groups)
GUI.EndGroup ();
GUI.EndGroup ();
}
}
在上面我们对控件布局的时候都是手动计算控件的位置和长宽,不仅很麻烦,而且对不同分辨率设备,这种根据像素点的空间参数可能会发生错误,因此我们需要寻求一种新的解决方案。
这个更好的解决方案就是GUI Layout。自动布局(Automatic Layout)。有时候开发者不太在乎控件的精确位置,而只是想让它们按一些简单方式显示出来就OK的情况,或者说,当你之前不知道需要多少元素,或者不想手动安置每个控件时,可以使用自动布局。比如,当您要根据保存游戏文件来创建若干不同按钮时,您可能不知道要绘制按钮的确切数目。在这种情况下使用自动布局会更方便。
如果希望使用自动布局,只需要使用GUlLayout类来替代前面例子中使用的GUI类,并去掉用Rect()指定的位置参数即可。
常见的GUILayout有四种:水平GUILayout、垂直GUILayout、滚动GUILayout和区域GUILayout。默认的是垂直方式的。
private void OnGUI()
{
GUILayout.BeginHorizontal();
GUILayout.Button("你好啊");
GUILayout.Button("你好啊");
GUILayout.Button("你好啊");
GUILayout.Button("你好啊");
GUILayout.EndHorizontal();
GUILayout.BeginVertical();
GUILayout.Button("你好啊");
GUILayout.Button("你好啊");
GUILayout.Button("你好啊");
GUILayout.Button("你好啊");
GUILayout.EndVertical();
}
private Vector2 sp;
private void OnGUI(){
sp = GUI.BeginScrollView(new Rect(0, 0, 200, 200), sp, new Rect(0, 0, Screen.width, 400));
GUI.Label(new Rect(100, 200, Screen.width, 50), "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
GUI.EndScrollView();
}
参数说明:
第一个rect是它的可见区域大小以及位置,sp储存的是横向的滚动条以及纵向的滚动条的初始值。
第二个rect储存的是整体视图的大小(不大于屏幕大小)。值得一提的是它后面还可以添加两个bool值。分别控制横纵滚动条在没有出现无法观看的内容的时候是否出现滚动条。
区域 (Areas) 只在自动布局 (Automatic Layout) 模式中使用。区域在功能上与固定布局群组类似,因为它们定义了屏幕中有限的一部分来容纳 GUILayout 控件。由于自动布局 (Automatic Layout) 特性的原因,您几乎会总是用到区域 (Area)。
在自动布局 (Automatic Layout) 模式中,也可以不在控件层 (Control level) 定义要绘制控件的屏幕区域。控件 (Control) 将被自动放置到容纳区域的最左上角。这有可能是屏幕。您也可以创建手动放置区域 (Areas)。区域内的 GUILayout 控件 (GUILayout Control) 将被放置到该区域的最左上角。
代码示例:
private 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();
}
注意,在一个区域 (Area) 内,带按钮 (Buttons)、框 (Boxes) 等可见元素的控件都会将其宽度拉伸至区域 (Area) 的全长。
如果想对自动布局的控件参数施加一些控制,可以使用GUlLayout参数。例如,当按钮上的文字很长的时候,使用GUILayout.Button绘制的按钮宽度会特别长,这时候可以传入GUlLayout参数来限制它的长度。如下:
private void OnGUI()
{
GUILayout.BeginArea(new Rect(100, 50, Screen.width - 200, Screen.height - 100));
GUILayout.Button("I am a regular Automatic Layout Button OK OK qwertyuiop[asdfghjkl;zxcvbnm");
GUILayout.Button("My width has been overridden", GUILayout.Width(95));
GUILayout.EndArea();
}
显示的效果为:
[1]仙峰游戏:周宝森. Unity教程:GUI界面开发. https://www.xf.com/gonglue/113801.2018.9.04
[2]博客园:Tonge.Unity3D深入浅出-GUI控件.
https://www.cnblogs.com/tonge/p/3935065.html.2014.8.27
[3]CSDN博客:wobushitankun. unity 3d GUI.BeginScrollView 介绍.
https://blog.csdn.net/wobushitankun/article/details/53510948.2016-12-7
[4]博客园:rainmj. 【Unity】8.3 布局模式(GUILayout).
https://www.cnblogs.com/rainmj/p/5437395.html.2016-04-27