4. IMGUI控件详解:Label、Button、TextField、Slider、Toggle等
好,咱们今天来聊聊IMGUI里最常用的那几个控件。说实话,IMGUI这套东西,你只要把基础控件玩熟了,编辑器工具开发就成功了一大半。我个人习惯是把这些控件分成两类:展示类和交互类。Label属于前者,剩下的Button、TextField、Slider、Toggle都属于后者。
4.1 Label:最朴素的文本展示
Label说白了就是一段只读文本。你不能点它,不能改它,它就安安静静待在那儿给你看信息。
// 最简单的用法
GUILayout.Label("这是一个标签");
// 带样式的标签
GUILayout.Label("红色警告", GUILayout.Width(200));
// 用GUIStyle定制
GUIStyle style = new GUIStyle();
style.fontSize = 16;
style.normal.textColor = Color.blue;
GUILayout.Label("蓝色大号文字", style);
我在项目中遇到过一个问题:Label的文字太长,把界面撑得乱七八糟。后来我养成了一个习惯——能用GUILayout.Width限制宽度的,绝不偷懒。你想想看,用户拖拽窗口大小的时候,Label自动换行,那个效果有多丑。
4.2 Button:点击触发逻辑
Button是交互的起点。它的返回值是bool,点击的那一帧返回true。
// 普通按钮
if (GUILayout.Button("保存配置"))
{
Debug.Log("用户点击了保存");
SaveConfig();
}
// 带图标的按钮
if (GUILayout.Button(EditorGUIUtility.IconContent("d_SaveActive"), GUILayout.Width(30)))
{
// 用内置图标
}
// 工具栏风格按钮
if (GUILayout.Button("刷新", EditorStyles.toolbarButton))
{
RefreshData();
}
嗯,这里要注意:Button的点击事件只在那一帧触发。我曾经见过新手把耗时操作直接写在if里面,结果界面卡死。正确的做法是——点击后启动协程,或者用EditorApplication.delayCall延迟执行。
4.3 TextField:文本输入
TextField需要你手动管理字符串变量。它返回的是用户输入后的新字符串。
private string playerName = "默认名称";
void OnGUI()
{
// 单行文本输入
playerName = GUILayout.TextField(playerName, 20); // 最大20字符
// 带标签的输入框
playerName = EditorGUILayout.TextField("玩家名称", playerName);
// 密码输入
string password = GUILayout.PasswordField(password, '*');
}
为什么TextField要返回新字符串?因为IMGUI是无状态的,它不保存任何控件的内部状态。每次OnGUI调用时,你都得把当前值传进去,它再把修改后的值返回给你。说白了就是——你自己管好自己的数据。
我在做资源命名工具时,用TextField让用户输入前缀。有个坑是:用户输入中文时,字符数计算会出问题。后来我改用System.Text.Encoding.UTF8.GetByteCount()来限制字节数,而不是字符数。
4.4 Slider:数值范围调节
Slider适合让用户在一个范围内调整数值。比如音量、速度、缩放比例等。
private float volume = 0.5f;
void OnGUI()
{
// 水平滑动条
volume = GUILayout.HorizontalSlider(volume, 0f, 1f);
// 带数值显示的滑动条
GUILayout.BeginHorizontal();
GUILayout.Label("音量", GUILayout.Width(50));
volume = GUILayout.HorizontalSlider(volume, 0f, 1f);
GUILayout.Label(Mathf.RoundToInt(volume * 100) + "%", GUILayout.Width(40));
GUILayout.EndHorizontal();
// EditorGUILayout的快捷方式
volume = EditorGUILayout.Slider("音量", volume, 0f, 1f);
}
我个人习惯用EditorGUILayout.Slider,因为它自带Label和数值显示,省得自己拼布局。但如果你需要自定义样式,那就老老实实用GUILayout.HorizontalSlider。
4.5 Toggle:开关切换
Toggle就是布尔值的开关。它返回的是切换后的bool值。
private bool isEnabled = true;
void OnGUI()
{
// 普通开关
isEnabled = GUILayout.Toggle(isEnabled, "启用功能");
// 带图标的开关
isEnabled = GUILayout.Toggle(isEnabled, EditorGUIUtility.IconContent("d_Toggle"));
// EditorGUILayout风格
isEnabled = EditorGUILayout.Toggle("自动保存", isEnabled);
// ToggleGroup(单选)
int selected = 0;
string[] options = {"选项A", "选项B", "选项C"};
selected = GUILayout.SelectionGrid(selected, options, 3);
}
Toggle有个兄弟叫ToggleGroup,用来做单选。但说实话,我更喜欢用GUILayout.SelectionGrid或者EditorGUILayout.EnumPopup来做单选,代码更清晰。
我曾经犯过一个错误:在Toggle的点击事件里修改了其他控件的状态,导致界面闪烁。后来发现是因为Toggle状态变化触发了OnGUI重绘,重绘时又修改了状态,形成了死循环。解决方案是用一个标志位延迟处理。
4.6 综合示例:一个简单的配置面板
来,咱们把上面这些控件串起来,做一个简单的工具配置面板。
public class ToolConfigWindow : EditorWindow
{
private string folderPath = "Assets/";
private float scale = 1.0f;
private bool autoBackup = true;
private int qualityLevel = 0;
[MenuItem("Tools/配置面板")]
static void OpenWindow()
{
GetWindow<ToolConfigWindow>("工具配置");
}
void OnGUI()
{
GUILayout.Label("基础设置", EditorStyles.boldLabel);
folderPath = EditorGUILayout.TextField("输出路径", folderPath);
scale = EditorGUILayout.Slider("缩放比例", scale, 0.1f, 5f);
autoBackup = EditorGUILayout.Toggle("自动备份", autoBackup);
GUILayout.Space(10);
GUILayout.Label("质量设置", EditorStyles.boldLabel);
string[] qualities = {"低", "中", "高"};
qualityLevel = GUILayout.SelectionGrid(qualityLevel, qualities, 3);
GUILayout.Space(10);
if (GUILayout.Button("应用设置"))
{
ApplyConfig();
}
}
void ApplyConfig()
{
Debug.Log($"路径:{folderPath},缩放:{scale},备份:{autoBackup},质量:{qualityLevel}");
}
}
这个例子涵盖了Label、TextField、Slider、Toggle、SelectionGrid和Button。你想想看,是不是大部分工具界面都能用这些控件拼出来?
4.7 控件选择速查表
| 控件 | 用途 | 返回值 | 常用场景 |
|---|---|---|---|
| Label | 展示文本 | 无 | 标题、说明、状态提示 |
| Button | 触发操作 | bool(点击帧为true) | 保存、刷新、执行 |
| TextField | 文本输入 | string | 路径、名称、参数 |
| Slider | 数值调节 | float | 音量、速度、大小 |
| Toggle | 开关切换 | bool | 启用/禁用、开关选项 |
嗯,这一章的内容就到这儿。记住一句话:IMGUI控件的核心就是“传值-接收返回值”这个循环。你只要理解了这一点,剩下的就是熟悉每个控件的参数和样式了。下一章咱们会深入讲布局,到时候你会发现,控件本身不难,难的是怎么把它们摆得好看又好用。