raylib raygui 即时模式 GUI:10 行滑块到参数面板再到滚动列表
2026/9/22 2:55:36 网站建设 项目流程

raylib raygui 即时模式 GUI:10 行滑块到参数面板再到滚动列表

【免费下载链接】raylibA simple and easy-to-use library to enjoy videogames programming项目地址: https://gitcode.com/GitHub_Trending/ra/raylib

你在写个小游戏,想加个面板让用户实时调参数。传统 GUI 框架要你维护状态树、挂回调、管事件队列。raylib 自带的 raygui 换个思路:每帧把控件当普通函数调用,直接传变量指针,画完就算完。这篇文章从 10 行代码开始,给你三条能落地的用法。

30 秒跑通

这段代码创建窗口,放一个滑块控制一个圆点的位置:

#define RAYGUI_IMPLEMENTATION #include "raygui.h" #include "raylib.h" int main(void) { InitWindow(320, 160, "raygui 最小示例"); float value = 0.5f; while (!WindowShouldClose()) { BeginDrawing(); ClearBackground(RAYWHITE); GuiSliderBar((Rectangle){ 20, 20, 280, 20 }, "参数", TextFormat("%.2f", value), &value, 0.0f, 1.0f); DrawCircle(20 + 280 * value, 100, 15, RED); EndDrawing(); } CloseWindow(); return 0; }

上面发生了什么:#define RAYGUI_IMPLEMENTATION让头文件自带实现,不用单独链接库。滑块绑上&value,你拖动它,value就跟着变,下面那行DrawCircle直接拿它当横坐标。没有控件树,没有回调,就是一个浮点数的变化。

机制拆解:为什么每帧都要调一遍控件

即时模式和保留模式的区别

保留模式 GUI 里,按钮是对象:你创建它、它自己记按下状态、事件触发时调你的回调。即时模式反过来,按钮每帧都是一次普通的函数调用。调用发生在BeginDrawing()EndDrawing()之间,raygui 在绘制控件的同时读鼠标键盘状态,完成高亮、焦点、取值这些交互逻辑。返回值代表"本帧有没有被点击",变量指针负责同步值。

// 返回值当"点击事件"用,不存控件对象 if (GuiButton((Rectangle){ 100, 300, 120, 36 }, "重置")) { value = 0.5f; }

好处是状态天然没有滞后:你改完value,下一帧滑块自动画到新位置,不存在"UI 没刷新"这种 bug。代价也明摆着:你每帧都得调一次,不调它就不存在。

场景一:用滑块控制游戏对象

先做最简单的联动:滑块值直接驱动绘制。

InitWindow(800, 450, "raygui 场景一:滑块联动"); float progress = 0.0f; while (!WindowShouldClose()) { BeginDrawing(); ClearBackground(RAYWHITE); // 滑块绑 progress,圆点位置跟着它走 GuiSliderBar((Rectangle){ 40, 40, 300, 24 }, "进度", TextFormat("%.2f", progress), &progress, 0.0f, 1.0f); DrawCircle(40 + 300 * progress, 200, 20, SKYBLUE); EndDrawing(); } CloseWindow();

注意第三参textRight:传TextFormat出来的字符串,滑块右端会实时显示当前值。想留白就传NULL

场景二:做一个参数面板

把滑块、复选框、下拉框、按钮装进一个GuiGroupBox,圆角矩形跟着参数实时变。

InitWindow(640, 400, "raygui 场景二:参数面板"); float width = 200.0f, corner = 0.1f; bool rounded = true; int colorIndex = 0; while (!WindowShouldClose()) { BeginDrawing(); ClearBackground(RAYWHITE); GuiGroupBox((Rectangle){ 10, 10, 300, 340 }, "圆角矩形参数"); GuiSliderBar((Rectangle){ 30, 44, 260, 24 }, "宽度", TextFormat("%.0f", width), &width, 50.0f, 320.0f); GuiSliderBar((Rectangle){ 30, 84, 260, 24 }, "圆角", TextFormat("%.2f", corner), &corner, 0.0f, 0.5f); // 下拉框带 editMode:false 时点击直接展开 GuiDropdownBox((Rectangle){ 30, 124, 260, 30 }, "红色;蓝色;绿色", &colorIndex, false); GuiToggle((Rectangle){ 30, 168, 120, 32 }, "启用圆角", &rounded); if (GuiButton((Rectangle){ 30, 214, 260, 32 }, "随机参数")) { width = 50.0f + 270.0f * (float)GetRandomValue(0, 100) / 100.0f; corner = 0.1f + 0.3f * (float)GetRandomValue(0, 100) / 100.0f; } Color colors[3] = { RED, BLUE, GREEN }; Rectangle target = { 340, 120, width, 200 }; // rounded 为 false 时退化成普通矩形,体现复选框作用 if (rounded) DrawRectangleRounded(target, corner, 8, colors[colorIndex]); else DrawRectangleRec(target, colors[colorIndex]); EndDrawing(); } CloseWindow();

面板控件全绑在普通变量上,if (GuiButton(...))里改变量,下一帧画布自动跟上。这就是即时模式的日常节奏。

场景三:画一张可滚动的实时列表

参数一多就放不下,用GuiScrollPanel加剪贴区处理。仓库里的examples/shapes/shapes_pie_chart.c就是这个路子,下面给一个缩小的等价版。

InitWindow(640, 480, "raygui 场景三:滚动面板"); #define PARAM_COUNT 12 float params[PARAM_COUNT]; bool editing[PARAM_COUNT] = { false }; char number[32] = { 0 };

滚动面板每帧算出可见区域,滑块的偏移量存进contentOffset,可见框存进view

Rectangle panel = { 20, 20, 600, 440 }; Rectangle content = { 0, 0, 600.0f, (float)PARAM_COUNT * 40 }; Vector2 contentOffset = { 0, 0 }; Rectangle view = { 0 }; GuiScrollPanel(panel, NULL, content, &contentOffset, &view);

BeginScissorMode把绘制裁进可见框,滑出界的行画了也看不见。每行一个滑块加一个数值输入框,两种操作改同一个数:

BeginScissorMode((int)view.x, (int)view.y, (int)view.width, (int)view.height); for (int i = 0; i < PARAM_COUNT; i++) { int rowY = (int)(view.y + contentOffset.y + i * 40); DrawText(TextFormat("P%02d", i + 1), (int)(view.x + 10), rowY + 8, 20, BLACK); GuiSliderBar((Rectangle){ view.x + 60, (float)rowY, 300, 28 }, NULL, NULL, &params[i], 0.0f, 100.0f); // 返回值 true 表示这帧该框被点中,翻转 editing 状态 if (GuiTextBox((Rectangle){ view.x + 380, (float)rowY, 120, 28 }, number, 32, editing[i])) editing[i] = !editing[i]; if (editing[i]) { params[i] = (float)Atoi(number); snprintf(number, 32, "%.0f", params[i]); } } EndScissorMode();

列表、编辑器、属性面板,套路都一样:滚轮管滚动,剪贴管越界,变量管数据。

踩坑速查

  • 整个界面突然全都不响应:某处调了GuiDisable(),它管到GuiEnable()为止,是全局状态。修法:把GuiEnable()写在if外面,保证一定执行。
  • 中文或 emoji 显示成方块:raygui 默认用 raylib 内置字体,只有 ASCII 码点。修法:LoadFontEx一个含目标字符集的字体,再GuiSetFont(f)
  • 控件贴着窗口边缘,一放大就切掉:坐标写死了 800x450 时代的值。修法:用GetScreenWidth() - 面板宽 - 边距这类相对算法定位置。
  • 下拉框传了四个参数报错GuiDropdownBoxGuiComboBox多一个editMode布尔参数,false 表示点击直接展开。
  • 程序直接崩溃GuiSliderBar第五参是float*,你塞了int*进去。修法:给滑块配一个float变量,展示时再强转。

接入清单

  1. 拿源码:git clone https://gitcode.com/GitHub_Trending/ra/raylib
  2. examples/shapes/raygui.h复制到你工程目录,和raylib.h放一起。
  3. 在你要用控件的那个 .c 文件里:
#define RAYGUI_IMPLEMENTATION #include "raygui.h"
  1. 编译时把它当普通源文件参与编译。控件每帧重画,不需要额外的 UI 初始化步骤。

写完了想接着学:去翻examples/shapes/shapes_pie_chart.cexamples/audio/audio_amp_envelope.c,一个练滚动面板加剪贴区,一个练滑块调音效参数的完整闭环。

【免费下载链接】raylibA simple and easy-to-use library to enjoy videogames programming项目地址: https://gitcode.com/GitHub_Trending/ra/raylib

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询