news 2026/9/9 18:10:48

raygui 从 0 到 1:给 C 语言游戏装上一块能拖能点的设置面板

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
raygui 从 0 到 1:给 C 语言游戏装上一块能拖能点的设置面板

raygui 从 0 到 1:给 C 语言游戏装上一块能拖能点的设置面板

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

C 游戏缺什么最影响观感?答案通常是音量、画质、难度这些"设置",而 raylib 附带的单文件即时模式 GUI 模块 raygui 就是补上这块的 C 语言 GUI 控件方案:一个头文件拷进项目,十几行代码就能画出一个滑块加一个开关。这篇 raygui 教程按"接入 → 跑通 → 排错 → 进阶"的顺序走一遍,所有 API 签名均已对照仓库内的raygui.h(v5.0)源码核实。

给 C 游戏装面板,raygui 为什么合适

先说结论:你要的状态全在自己变量里,raygui 只负责每帧把变量画出来、再读一次输入写回去。

把 raygui 想成点餐小票:厨房不记单,每来一单都重打一张,照着做。即时模式 GUI 同理——控件没有实例,不注册、不销毁、不维护 ID,声明它的代码跑过去的那一刻,它就被画出来了。上一帧的控件和这一帧的控件是两个"不同的人",唯一连续存在的是你传给它的那几个变量。

这套模型带来两个直接好处:

  • 不用管理控件生命周期,删除界面上的一块区域,删掉对应的声明即可;
  • 布局没有自动排版(raygui.h头注释里明确写了 "No auto-layout mechanism"),每个控件的位置尺寸都要你自己算,换来的是完全可控。

从仓库把 raygui.h 接进项目

raylib 仓库里raygui.h放在examples/的各模块目录下,每个模块各有一份内容相同的副本,实际用时拷一份到自己项目即可。

// panel.c —— 整个工程里只有这一处定义宏 #define RAYGUI_IMPLEMENTATION // 把 raygui 的实现编译进本翻译单元 #include "raygui.h"

RAYGUI_IMPLEMENTATION必须在有且仅有一个.c文件里写,实现代码就从这个宏后面展开;其他源文件只写#include "raygui.h",拿到的是声明。编译时照常链接 raylib,例如gcc demo.c panel.c -lraylib -o demo。仓库地址是 https://gitcode.com/GitHub_Trending/ra/raylib ,clone 下来即可。

第一条滑块:最小可运行示例

单文件、能直接跑的版本长这样:

#include "raylib.h" #define RAYGUI_IMPLEMENTATION #include "raygui.h" int main(void) { InitWindow(520, 300, "raygui demo"); float g_vol = 0.35f; // 0~1 音量 bool g_speakerOn = true; // 喇叭开关 while (!WindowShouldClose()) { BeginDrawing(); ClearBackground(LIGHTGRAY); int pct = (int)(g_vol * 100); GuiSliderBar((Rectangle){ 48, 64, 240, 26 }, "VOL", TextFormat("%d%%", pct), &g_vol, 0.0f, 1.0f); GuiCheckBox((Rectangle){ 48, 118, 24, 24 }, "SPEAKER", &g_speakerOn); EndDrawing(); } CloseWindow(); return 0; }

GuiSliderBar的四个参数依次是:位置尺寸(Rectangle)、左端标签、右端文本、数值指针加取值区间,返回值非 0 表示这一帧滑块被碰过。GuiCheckBox更简单,指针指向那个bool,复选框的勾选状态就存在变量里。跑起来后拖一下滑块:变量在变,画面就跟着变,没有其他任何"同步"代码。

控件隔帧就失忆:即时模式的记账规则

很多人第一次接触即时模式 GUI 时的疑问是:控件不存状态,那它凭什么"记得"我上次拖到哪?答案是:它不记,也不需要记,你每帧把变量的当前值递过去,画出来的就是这个值。

这也解释了三个容易踩的点:

  • 想让下拉框展开、让文本框处于编辑态,开合状态得由你自己用一个bool存着、每帧回传;
  • 界面上任何"记住上一步"的效果,实现方式都是"上一帧把结果写进变量,这一帧变量再画出来";
  • 控件返回的永远是"本帧发生了什么",不是一段持续的历史,想触发一次性动作就用它的返回值判断。

按交互目的选控件:一张速查表

选型只看一件事:用户要做哪种动作。四种最常用的 C 语言 GUI 控件用法如下(均摘自各控件的函数签名):

交互目的控件一行调用
连续数值(0~1 音量、半径)滑块GuiSliderBar(rec, "VOL", TextFormat("%d%%", pct), &g_vol, 0.0f, 1.0f);
开 / 关切换复选框GuiCheckBox(rec, "SPEAKER", &g_speakerOn);
按钮形态的开关,适合面板角落开关按钮GuiToggle(rec, "MUTE", &g_mute);
有限选项三选一下拉框GuiDropdownBox(rec, "LOW;MID;HIGH", &g_quality, false);
整数加减速(1~10 切片数)步进器GuiSpinner(rec, "SLICES", &g_slices, 1, 10, false);

两个细节值得留意:滑块右端的数字显示不会自动生成,传NULL就不画,传文本就显示;下拉框的选项是用分号连起来的一个字符串,选中项是int下标,从 0 开始。

返回值语义各控件略有差别,但思路一致。GuiCheckBox在用户真正勾选/取消的那一帧才返回 1,所以if (GuiCheckBox(...)) PlaySoundClick();这种写法天然是"点击瞬间触发一次"。而GuiDropdownBox的第四参editMode语义不同——传false时它自己管理开合,点击展开、松开收起,调用方只管读指针里的选中项;传true则强制保持展开,配合手动翻转的bool可以做常驻展开的列表头。

实战样本:饼图示例右侧的整块控制台

仓库examples/shapes/shapes_pie_chart.c是 raygui 用法的集大成示例:右侧 270 像素宽的面板里,GuiSpinner调切片数、三个GuiCheckBox切换数值/百分比/环形显示、GuiSliderBar控制内环半径,下方GuiScrollPanel里每行还嵌了一个文本框加一个滑块。

读这个文件最大的收获是看清职责边界:切片扇形是用DrawCircleSector自己算角度画的,raygui 只管面板上那些"问用户要参数"的控件;而shapes_splines_drawing.c则展示了另一种混排——样条控制点靠GetMousePosition()自己做命中检测,和下拉框、滑块和平共处。记住这条线:输入与控件渲染归 raygui,业务绘制归你。

每行一个编辑器的列表:滚动面板加剪切区

控件多到放不下时,仓库示例给出了标准答案:GuiScrollPanel给出可视区,BeginScissorMode裁掉滚出屏幕的内容,内容按滚动偏移量平移:

int scroll = 0; // 当前滚动偏移 int h = rows * 34; // 内容总高 GuiScrollPanel((Rectangle){ 20, 40, 220, 200 }, NULL, (Rectangle){ 0, 0, 210, (float)h }, &scroll, &view); BeginScissorMode((int)view.x, (int)view.y, (int)view.width, (int)view.height); for (int i = 0; i < rows; i++) { int y = (int)(view.y + 5 + i * 34 + scroll); if (y > view.y && y < view.y + view.height) GuiSliderBar((Rectangle){ view.x + 10, (float)y, 190, 26 }, NULL, NULL, &values[i], 0.0f, 1.0f); } EndScissorMode();

view是可视区在内容坐标下的窗口,scroll每帧由滚动条写回;行循环里那句if是滚动列表的通用优化:滚出视野的行直接跳过声明,反正即时模式没有"不画就不存在"的成本,跳过就是省。这套模式可以原样搬到参数编辑器、音量混音台这类"一行一个值"的界面上。

窗口拉大后控件不跟手的解法

硬编码坐标的面板一拉窗口就错位。修法是把"面板锚在哪条边"写成每帧重算的表达式:

int winW = GetScreenWidth(), winH = GetScreenHeight(); const int panelW = 240; float ax = winW - panelW - 14; // 右缘锚点 float ay = winH - 170 - 14; // 底缘锚点 GuiSliderBar((Rectangle){ ax + 18, ay, (float)(panelW - 36), 24 }, "LV", TextFormat("%d", g_level), &g_level, 1.0f, 5.0f);

两个要点:所有控件坐标都从ax/ay派生,窗口尺寸一变,下一帧自动落回正确位置;开了高分屏缩放的平台上,GetScreenWidth/Height返回的已经是逻辑分辨率,别再手动乘 DPI 系数。如果面板外还有可拖拽的自定义实体,每帧拿GetMousePosition()减去实体自己的包围矩形做命中检测即可,raygui 不会挡住你自己写的输入逻辑。

文字超框、中文变方块、分配抖动:三个高频 raygui 踩坑

这三个坑几乎人人会遇到,对应的修复都是一行级:

  • 文字贴边或画出框外 → 默认字号配 20 像素高的控件确实挤,GuiSetStyle(DEFAULT, DEFAULT_TEXT_ALIGNMENT, TEXT_ALIGN_CENTER)调对齐,或把控件高度加大几像素;
  • 中文显示成方块 → 内置默认字体不含 CJK 字形,GuiSetFont(LoadFontEx("cjk.ttf", 22, 0, 256));换一张带中文的字库,全局生效;
  • 每帧TextFormat导致堆分配频繁 → 把结果缓存进静态char数组,只在数值真正变化时重算,面板帧率立刻稳下来。

收尾:变量为准,然后去改一套主题

raygui 的设计哲学一句话:控件不保存状态,每帧声明、变量为准——布局、主题、开合状态全部写在你自己的代码里,删了重写没有任何心理负担。接下来值得花一个下午的事:把GuiSetStyle的三个参数翻一遍,给滑块、复选框、下拉框各换一组颜色与字号,或者照着饼图示例的滚动列表,给自己的游戏做一个参数编辑器。

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

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 18:10:33

SillyTavern 桌面打包指南:用 Electron 双击启动 LLM 聊天前端

SillyTavern 桌面打包指南&#xff1a;用 Electron 双击启动 LLM 聊天前端 【免费下载链接】SillyTavern LLM Frontend for Power Users. 项目地址: https://gitcode.com/GitHub_Trending/si/SillyTavern 每次用 SillyTavern 都要开终端、找目录、敲启动命令&#xff1f…

作者头像 李华
网站建设 2026/9/9 18:10:20

浮点数精度陷阱:从IEEE 754到实际工程中的比较与误差控制

要理解浮点数为什么会在编程里反复误导人&#xff0c;先从一个最常见的画面说起&#xff1a;你在 IDE 里写了一段判断两个浮点数是否相等的代码&#xff0c;左看右看&#xff0c;逻辑都符合常识&#xff0c;编译也没有报错&#xff0c;可一跑起来&#xff0c;结果就是跟你预期的…

作者头像 李华
网站建设 2026/9/9 18:09:07

6GB显存跑通AI 3D:从单图生成到虚幻引擎游戏角色完整指南

在实际接手 AI 3D 项目时&#xff0c;最容易低估的不是模型效果&#xff0c;而是显存。“AI 3D”这个关键词背后&#xff0c;通常是一整套从单图生成网格、再到引擎可用资产的处理链路。对只有 6GB 显存的开发者来说&#xff0c;问题尤其明显&#xff1a;生成工具的默认参数往往…

作者头像 李华
网站建设 2026/9/9 18:09:01

CPU热插拔与线程绑核:从强制迁移到亲和性失效的全解析

半夜两点被值班电话叫起来&#xff0c;说某台服务器上有个CPU核心温度异常&#xff0c;得马上把那一核给隔离掉。业务团队在旁边急得不行&#xff0c;因为他们的核心线程全部绑在了那颗核上&#xff0c;谁也不知道把CPU offline之后线程会不会直接崩。这个场景我相信不少做性能…

作者头像 李华