12.总结
12.1 知识点

学习的主要内容

优点

缺点

主要用处

12.2 核心要点速览
IMGUI 的定位、用途与 OnGUI
- 名称习惯:文中把 GUI 当作 Unity 里对 IMGUI(即时模式游戏用户交互界面)的简称;控件在代码里即时声明,属于代码驱动的 UI 写法。
- 三条主用途:运行时给程序员做调试工具与游戏内 Debug 面板;给脚本写 Inspector 自定义检视;做编辑器窗口和内置工具,扩展引擎工作流。
- 红线:不要拿它给玩家做正式、量产的界面功能;玩家向界面通常交给 UGUI、UI Toolkit 等方案。
- 挂载方式:在挂到对象上的
MonoBehaviour里实现OnGUI(),在其中调用GUI等 API,语义上类似生命周期里的一个绘制阶段。
| 要点 | 说明 |
|---|---|
| 调用节奏 | 按文中所述每帧进入,专门承担「这一帧 GUI 画什么、点什么」的职责。 |
| 书写边界 | 尽量只做 GUI 绘制与轻量交互逻辑,别把整套玩法运算堆进回调。 |
| 相对顺序 | 在 LateUpdate 之后、OnDisable 之前执行到 OnGUI。 |
| 谁能写 | 任意继承 MonoBehaviour 的脚本都可以声明并实现 OnGUI。 |
private void OnGUI()
{
// 在其中书写 GUI 相关代码即可显示 GUI 内容
}
IMGUI 控件调用共性
- 静态入口:常用控件都在
GUI上,静态方法即调即用。 - 坐标与尺寸:第一参数多半是
Rect(x、y、width、height);原点在该系列示例语境下取 Game 视图左上角,向右、向下为正。 - 参数组合习惯:展示用
string、Texture或包了一层的GUIContent;要换外观再传GUIStyle。多组重载本质是上述几类参数排列,位置和展示内容是最少要凑齐的两样。
文本控件(Label)
作用:生成文本控件。
tooltip:
GUI.tooltip为只读;本帧画完控件后,可取当前鼠标悬停或键盘焦点控件对应的 tooltip(与GUIContent里配的 tooltip 一起用才有内容)。重载形式:
public static void Label(Rect position, string text);public static void Label(Rect position, Texture image);public static void Label(Rect position, GUIContent guiContent);public static void Label(Rect position, string text, GUIStyle style);public static void Label(Rect position, Texture image, GUIStyle style);public static void Label(Rect position, GUIContent guiContent, GUIStyle style);
使用示例:
GUI.Label(new Rect(100, 20, 100, 20), "666666欢迎你"); GUI.Label(labelRect1, image); GUI.Label(labelRect2, guiContent); GUI.Label(new Rect(500, 200, 500, 500), "林文韬欢迎你", labelGuiStyle);
按钮控件
普通按钮(Button)
作用:创建单击按钮,点击时执行操作,返回布尔值。
点击判定:在按钮矩形内按下鼠标,并在仍算同一次点击的抬起发生在合理命中范围内,才得到一次
true;在按钮内按下却到按钮外抬起,或反过来,都不算一次有效点击。重载形式:与 Label 类似,有多种参数组合。
使用示例:
if (GUI.Button(btnRect, btnGUIContent1, btnGuiStyle)) { Debug.Log("按钮被点击"); }
长按按钮(RepeatButton)
作用:用户按住时一直处于激活状态,返回布尔值。
范围:按住期间若指针移出控件矩形,离开瞬间就会变回
false,不会一直为true。使用示例:
if (GUI.RepeatButton(new Rect(300, 60, 100, 20), btnGUIContent2)) { Debug.Log("长按按钮被点击"); }
多选框(Toggle)
作用:创建打开/关闭的开关按钮,需传入布尔值表示选中状态,返回改变后的选中状态。
样式:自定义
GUIStyle时若显示挤压、对不齐,常见是调fixedWidth、fixedHeight和padding。重载形式:与 Label 类似,有多种参数组合。
使用示例:
isSel = GUI.Toggle(new Rect(0, 0, 100, 30), isSel, "效果开关"); isSel2 = GUI.Toggle(new Rect(0, 40, 100, 30), isSel2, "音效开关", style);
单选框(基于 Toggle 实现)
关键:通过一个 int 标识决定是否选中,改变 int 值。
使用示例:
if (GUI.Toggle(new Rect(0, 100, 100, 30), nowSelIndex == 1, "选项一")) { nowSelIndex = 1; } if (GUI.Toggle(new Rect(0, 140, 100, 30), nowSelIndex == 2, "选项二")) { nowSelIndex = 2; } if (GUI.Toggle(new Rect(0, 180, 100, 30), nowSelIndex == 3, "选项三")) { nowSelIndex = 3; }
输入框
普通输入框(TextField)
作用:创建可供用户编辑字符串的单行文本字段。
长度限制:传入最大长度后,超出部分无法再输入,需把返回值持续赋回字符串变量才能看到编辑结果。
使用示例:
inputStr = GUI.TextField(new Rect(0, 0, 100, 30), inputStr, 5);
密码输入框(PasswordField)
作用:创建可让用户输入密码的文本字段。
使用示例:
inputPW = GUI.PasswordField(new Rect(0, 50, 100, 30), inputPW, '★');
拖动条
水平拖动条(HorizontalSlider)
作用:用户可拖动的水平滑动条,用于在最小值和最大值之间更改某值。
使用示例:
nowValue = GUI.HorizontalSlider(new Rect(0, 100, 100, 50), nowValue, 0, 1);
竖直拖动条(VerticalSlider)
作用:用户可拖动的垂直滑动条,用于在最小值和最大值之间更改某值。
使用示例:
nowValue = GUI.VerticalSlider(new Rect(0, 150, 50, 100), nowValue, 0, 1);
图片绘制(DrawTexture)
作用:在一个矩形内绘制纹理。
重载形式:
public static void DrawTexture(Rect position, Texture image);public static void DrawTexture(Rect position, Texture image, ScaleMode scaleMode);public static void DrawTexture(Rect position, Texture image, ScaleMode scaleMode, bool alphaBlend);public static void DrawTexture(Rect position, Texture image, ScaleMode scaleMode, bool alphaBlend, float imageAspect);
参数说明:
- ScaleMode:图像宽高比不适合绘制宽高比时的缩放方式,包括 ScaleAndCrop(计算后裁剪)、ScaleToFit(保持完整显示)、StretchToFill(填充满整个区域)。
- alphaBlend:绘制图像时是否应用 Alpha 混合,控制透明通道。
- imageAspect:源图像宽高比,为 0 时使用图像默认宽高比。
使用示例:
GUI.DrawTexture(texPos, tex, mode, alpha, wh);
框绘制(Box)
作用:在 GUI 层上创建一个框。
使用示例:
GUI.Box(texPos, "123");
工具栏(Toolbar)
作用:创建一个工具栏,需要一个 int 用于记录当前索引和一个 string 数组用于显示内容。返回新的选中索引,记得写回
int,否则选中状态不更新。示例:
int toolbarIndex = 0; string[] toolbarInfos = { "选项1", "选项2", "选项3" }; toolbarIndex = GUI.Toolbar(new Rect(0, 0, 200, 30), toolbarIndex, toolbarInfos); switch (toolbarIndex) { case 0: // 逻辑处理 break; case 1: // 逻辑处理 break; case 2: // 逻辑处理 break; }
选择网格(SelectionGrid)
作用:创建一个按钮网格,需要一个 int 索引、string 数组内容和
xCount(每行最多放几个);同样要把返回的索引赋回变量。示例:
int selGridIndex = 0; string[] gridInfos = { "选项1", "选项2", "选项3" }; selGridIndex = GUI.SelectionGrid(new Rect(0, 50, 200, 60), selGridIndex, gridInfos, 2);
分组(BeginGroup 和 EndGroup)
作用:批量控制控件位置,类似父对象容器。
示例:
GUI.BeginGroup(groupRect); GUI.Button(new Rect(0, 0, 100, 50), "测试按钮"); GUI.Label(new Rect(0, 60, 100, 20), "Label信息"); GUI.EndGroup();
滚动列表(BeginScrollView 和 EndScrollView)
作用:创建可滚动视图。
参数说明:
- position:视口在屏幕上的矩形,含滚动条占位。
- scrollPosition:内容偏移,必须用
BeginScrollView的返回值写回该变量,否则滚动状态留不住。 - viewRect:逻辑内容区域大小;内容比视口高或宽才会出现滚动;动态列表常按条数乘行高改
height。
示例:
nowPos = GUI.BeginScrollView(scRect, nowPos, showRect); // 内容 GUI.EndScrollView();
窗口
普通窗口(Window)
作用:创建弹出窗口。
参数:
- id:窗口唯一标识,多个窗口可共用一个绘制函数,在回调里用
id分支。 - position:位置和大小;要做拖动窗口,一般把
Rect存成成员字段,每帧把GUI.Window的返回值赋回该字段。 - function:绘制内容的回调。
- id:窗口唯一标识,多个窗口可共用一个绘制函数,在回调里用
示例:
private void OnGUI() { GUI.Window(1, new Rect(100, 100, 200, 150), DrawWindow, "测试窗口"); GUI.Window(2, new Rect(100, 250, 200, 150), DrawWindow, "测试窗口2"); } private void DrawWindow(int id) { switch (id) { case 1: GUI.Button(new Rect(0, 30, 30, 20), "1"); break; case 2: GUI.Button(new Rect(0, 30, 30, 20), "2"); break; case 3: GUI.Button(new Rect(0, 30, 30, 20), "3"); break; } }
模态窗口(ModalWindow)
作用:显示模态窗口,其它 IMGUI 控件暂时点不到,优先处理当前弹层,适合做必须点掉的提示。
示例:
GUI.ModalWindow(3, new Rect(300, 100, 200, 150), DrawWindow, "模态窗口");
拖动窗口
作用:实现窗口可拖动。
配合:外层用成员
Rect接GUI.Window返回值;在窗口函数里调用GUI.DragWindow,无参则整窗可拖,传入Rect则只有那块区域能拖(例如标题条)。示例:
dragWinPos = GUI.Window(4, dragWinPos, DrawWindow, "拖动窗口"); private void DrawWindow(int id) { switch (id) { case 4: GUI.DragWindow(new Rect(0, 0, 1000, 20)); break; } }
全局颜色和皮肤样式
全局颜色
GUI.color:全局着色,相当于给后续 GUI 绘制套一层 tint。
GUI.contentColor:作用在文本上,与
GUI.color相乘。GUI.backgroundColor:作用在背景元素上,同样与
GUI.color相乘;三个量都动过以后,最终观感是叠乘结果,排查偏色时记得从这三处一起往回捋。示例:
GUI.color = Color.red; GUI.Label(new Rect(0, 50, 150, 30), "全局着色颜色标签红"); GUI.contentColor = Color.yellow; GUI.Button(new Rect(0, 200, 150, 30), "文本着色颜色"); GUI.backgroundColor = Color.green; GUI.Button(new Rect(0, 250, 150, 30), "背景元素着色颜色", style);
整体皮肤样式
创建:在 Project 窗口右键创建
GUISkin资源,拖到脚本字段再在OnGUI里赋给GUI.skin。使用:
GUI.skin = mySkin管的是「没单独指定样式时」的默认外观;调用时若显式传了GUIStyle,以该参数为准,当前 skin 对这类绘制不起作用。换皮前:先把
GUI.color、GUI.contentColor、GUI.backgroundColor设回白色,再套皮肤,避免上一轮全局着色把新皮肤「染花」。收尾:
GUI.skin = null回到内置默认皮肤。示例:
GUI.color = Color.white; GUI.contentColor = Color.white; GUI.backgroundColor = Color.white; GUI.skin = mySkin; GUI.Button(new Rect(200, 0, 100, 30), "测试按钮1"); GUI.Button(new Rect(200, 50, 100, 30), "测试按钮2", style); GUI.skin = null; GUI.Button(new Rect(200, 100, 100, 30), "测试按钮3");
GUILayout 自动布局
定位:API 形态与
GUI接近,由布局系统算控件矩形;篇中写明更偏编辑器扩展,拿来堆正式游戏 UI 不合适。和
GUI混用:常见写法是外层GUI.BeginGroup定一块总区域,内层GUILayout.BeginArea再开自动布局区间,竖直BeginVertical/ 水平BeginHorizontal成对闭合,最后EndArea、EndGroup顺序别写反。特点:省掉大量手写
Rect对齐,复杂排版仍要配合GUILayoutOption。示例:
GUI.BeginGroup(new Rect(100, 100, 500, 300)); GUILayout.BeginArea(new Rect(10, 10, 400, 300)); GUILayout.BeginVertical(); GUILayout.Button("竖直123", GUILayout.Width(200)); GUILayout.Button("竖直245666656565"); GUILayout.Button("竖直235", GUILayout.ExpandWidth(false)); GUILayout.EndVertical(); GUILayout.BeginHorizontal(); GUILayout.Button("水平123", GUILayout.Height(300)); GUILayout.Button("水平245666656565"); GUILayout.Button("水平235", GUILayout.ExpandWidth(false)); GUILayout.EndHorizontal(); GUILayout.EndArea(); GUI.EndGroup();
GUILayoutOption 布局选项
- 用法:作为
GUILayout.Button、GUILayout.Label等方法的可变参数传入才生效;单独写一行GUILayout.Width(300)而不传给控件,不会产生布局效果。 - GUILayout.Width, GUILayout.Height:固定宽高。
- GUILayout.MinWidth, GUILayout.MinHeight:最小宽高。
- GUILayout.MaxWidth, GUILayout.MaxHeight:最大宽高。
- GUILayout.ExpandWidth, GUILayout.ExpandHeight:允许或禁止在对应方向上把剩余空间吃满。
12.3 面试题精选
基础题
1. Unity 里的 GUI 与 IMGUI 是什么关系,常见用在哪
题目
篇中如何定义 GUI / IMGUI?列举三类主要用途,并说明作者明确不建议的使用场景。
深入解析
- 定义:GUI 即 IMGUI(即时模式、代码驱动);Unity 里常直接叫 GUI。
- 用途:游戏内调试工具;脚本自定义检视面板;编辑器窗口与扩展 Unity 的内置工具。
- 禁忌:不要为玩家制作正式 UI 功能(交付界面应交给更合适的 UI 技术栈)。
答题示例
这里的 GUI 就是 IMGUI,用代码即时描述界面。
典型用在 Debug 面板、自定义 Inspector、编辑器工具;不适合做给玩家的成品界面。
参考文章
- 2.工作原理和主要作用
2. Button 和 RepeatButton 什么时候为 true
题目
GUI.Button 怎样才算点到一次?GUI.RepeatButton 按住不放时一直为 true 吗,指针移出控件会怎样?
深入解析
Button:在控件范围内完成一次有效的按下—抬起组合才在抬起当帧得到true;按下与抬起若跨区域组合,篇中明确说不会得到true。RepeatButton:按住且指针仍在控件内时持续true;按住过程中移出矩形,立刻变为false。
答题示例
Button 要在按钮里按下并正常完成一次点击才 true,内外混按不算。
RepeatButton 按住在内才一直 true,拖出范围马上 false。
参考文章
- 3.基础控件-文本和按钮控件
3. 设置了 GUI.skin 为什么有的按钮还是不走皮肤
题目
篇中示例里,先 GUI.skin = mySkin,为什么有的 GUI.Button 仍然表现为自定义 GUIStyle,而不是皮肤里的默认按钮样式?
深入解析
- 控件调用里显式传入
GUIStyle参数时,以该样式为准,当前GUI.skin不会覆盖这次绘制。 - 只有未单独指定
GUIStyle的那类调用,才会吃到GUI.skin里配置的默认样式。
答题示例
传了 GUIStyle 的那次 Button 用参数样式,不吃全局 skin。
没传 GUIStyle 的才用当前 GUI.skin 的默认外观。
参考文章
- 10.自定义整体样式-自定义皮肤样式
进阶题
1. OnGUI 的回调节奏与生命周期位置
题目
篇中对 OnGUI 的执行频率、在生命周期中的相对顺序有什么结论?写 OnGUI 时应遵守什么书写习惯?
深入解析
- 频率:每帧执行,专门承担 GUI 绘制与交互入口的角色。
- 顺序:位于
LateUpdate之后、OnDisable之前。 - 习惯:回调里以 GUI 绘制与界面相关逻辑为主,避免塞无关重逻辑,便于维护和排查。
答题示例
OnGUI每帧跑,在LateUpdate后、OnDisable前。里面主要写画界面和处理按钮这类 GUI 逻辑,别当通用
Update用。
参考文章
- 2.工作原理和主要作用
2. BeginScrollView 三个矩形和 scroll 向量各自干什么
题目
GUI.BeginScrollView 的视口矩形、传入的 scrollPosition、viewRect 分别控制什么?为什么代码里常见 nowPos = GUI.BeginScrollView(..., nowPos, ...) 必须把返回值赋回去?
深入解析
- 视口矩形:滚动视图在屏幕上的可见区域(含滚动条区域)。
scrollPosition:内容相对视口的偏移,引擎根据交互改它,不取返回值写回变量则下一帧状态对不上。viewRect:内容逻辑尺寸;小于视口不出现滚动,大于视口才需要拖滚动条;动态列表常按条目数乘行高调整高度。
答题示例
外面那个 Rect 是视口,viewRect 是整块内容有多大,scroll 是内容挪了多少。
BeginScrollView 会改滚动偏移,必须赋回字段或变量,不然滚不动或乱跳。
参考文章
- 8.组合控件-滚动视图和分组
3. GUILayout 适合干什么,和 GUI.BeginGroup 怎么配合
题目
按课文,GUILayout 主要适合哪类工作?示例里 BeginGroup、BeginArea、BeginVertical / BeginHorizontal 大致是什么包含关系?
深入解析
- 课文把 GUILayout 定位在编辑器向自动排版;与手写
Rect的GUI相比,少抠坐标,但不等于能替代 UGUI 等玩家界面方案。 - 结构上:先用
GUI.BeginGroup圈总区域,再用GUILayout.BeginArea在组内划自动布局画布,竖直或水平组再往里排控件;EndVertical/EndHorizontal、EndArea、EndGroup依次配对弹出。
答题示例
GUILayout 适合编辑器里省 Rect,课文说不适合扛正式游戏 UI。
Group 包大块,Area 里再竖直或水平排控件,End 顺序和 Begin 反着关。
参考文章
- 11.自定义整体样式-自动布局
深度题
1. 为什么不宜用 IMGUI 做玩家正式界面
题目
结合篇中「三条主要用途 + 一条注意」,从定位上说明为什么 IMGUI 更适合工具链与调试,而不是玩家可见的正式 UI。
深入解析
- 原文把能力圈在调试、Inspector、编辑器扩展,并单独强调不要服务玩家 UI,说明其设计重心在研发与工具链,而不是玩家可见的正式界面。
- 立即模式、代码驱动意味着界面主要在脚本里逐帧描述;玩家向界面往往还要接美术资源、布局复用、多分辨率适配等工程需求,专用 UI 方案通常更合适。
- 工程上玩家 UI 常见 UGUI、UI Toolkit 等,把 IMGUI 留在 Debug、Inspector、Editor 扩展更符合原文定位。
答题示例
文章把 GUI 用在 Debug、Inspector、编辑器工具,并写明别给玩家做 UI。
它是给开发和工具用的即时绘制;玩家界面要走资源与布局更成熟的 UI 系统,否则维护和适配成本不划算。
参考文章
- 2.工作原理和主要作用
2. 可拖动窗口为什么要用成员 Rect 接 Window 返回值
题目
IMGUI 里做可拖窗口时,为什么通常把窗口矩形存成字段,并写 dragWinPos = GUI.Window(id, dragWinPos, DrawWindow, title)?GUI.DragWindow 写在哪儿、无参和有参 Rect 各是什么效果?
深入解析
- 拖动会改变窗口位置,必须把每帧算出的新矩形保存到成员里,下一帧才能接着从正确位置画。
DragWindow只在窗口绘制回调里调用才生效;无参表示整窗可拖,带Rect时只有该区域作为拖动手柄(例如标题栏高度一条)。
答题示例
位置会变就要存字段里,每帧用 Window 返回值更新。
DragWindow 写在窗口函数里;不传 Rect 全窗能拖,传了 Rect 只有那块区域能拖。
参考文章
- 9.组合控件-窗口相关
3. 换 GUISkin 前为什么先把三种颜色设回白色
题目
课文在 GUI.skin = mySkin 之前把 GUI.color、GUI.contentColor、GUI.backgroundColor 都设成白色,这和「颜色相乘」有什么关系?若跳过这一步可能出现什么现象?
深入解析
contentColor、backgroundColor会与GUI.color相乘作用到最终绘制;上一轮若把全局色留在红、黄、绿等状态,再套新皮肤等于整皮被旧 tint 染一层。- 先归零到白色再换 skin,才能较接近资源里设计好的 GUISkin 观感,排查样式问题时也少一层干扰。
答题示例
三种颜色是相乘关系,不先设白,旧的全局着色会染在新皮肤上。
先全部 white 再 GUI.skin,看到的才接近皮肤资源本身。
参考文章
- 10.自定义整体样式-自定义皮肤样式
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com