raylib 的 raygui:20 行 C 代码搭出可拖动的游戏 GUI 面板
【免费下载链接】raylibA simple and easy-to-use library to enjoy videogames programming项目地址: https://gitcode.com/GitHub_Trending/ra/raylib
你的游戏缺一个能拖能点的设置面板,又不想为了几根滑块引入一整套 UI 框架。raygui 是 raylib 游戏库自带的即时模式 GUI 模块,一个单文件头就能用。读完这篇,你能在 C 项目里跑出滑块、复选框、下拉框,并把一整块控制面板钉在窗口边缘。
先跑起来
#include "raylib.h" #include <stdio.h> #define RAYGUI_IMPLEMENTATION // 全项目只在一个 .c 里这样写 #include "raygui.h" int main(void) { InitWindow(640, 360, "raygui minimal"); float volume = 60.0f; bool soundOn = true; while (!WindowShouldClose()) { BeginDrawing(); ClearBackground(RAYWHITE); char buf[32]; snprintf(buf, 32, "vol %d", (int)volume); // 本帧被拖动则返回非 0,新值写回 &volume GuiSliderBar((Rectangle){ 40, 60, 240, 24 }, "音量", buf, &volume, 0.0f, 100.0f); // 返回 1 的那一帧,正好是勾选状态改变的那一帧 if (GuiCheckBox((Rectangle){ 40, 100, 22, 22 }, "声音", &soundOn)) PlaySoundClick(); char dbg[32]; snprintf(dbg, 32, "soundOn = %d", soundOn); DrawText(dbg, 40, 150, 20, BLACK); EndDrawing(); } CloseWindow(); return 0; }三句话点评,全是坑位:RAYGUI_IMPLEMENTATION只在一个.c文件里定义,写两处会直接链接报重复符号;每个控件第一参数都是Rectangle,长什么样不重要,在哪儿、多大完全由这个矩形说了算;控件返回值是"本帧有没有被操作",新值通过指针参数回写,所以&volume传的是指针,不是值——想传&60.0f?编译器会拦你,但你心里得清楚这是规矩。
原理一句话
把它想成餐厅服务员:每帧他拿小票过来,记下你的变量值,交给后厨画出来,画完小票撕掉——没有常驻控件,也没有状态机。所以你的变量就是唯一真相:60 FPS 下所有滑块"每帧重建"并不浪费,声明本身就是渲染,这就是即时模式的全部秘密。控件删了重写都不怕乱,因为乱不了。
按目标选控件
让数值连续可调
调音量、难度、透明度这类量,用滑块;要整数步进,用 spinner。
float master = 0.8f; int level = 3; GuiSliderBar((Rectangle){ 40, 80, 220, 20 }, "主音量", TextFormat("%d%%", (int)(master * 100)), &master, 0.0f, 1.0f); GuiSpinner((Rectangle){ 40, 120, 140, 30 }, "难度", &level, 1, 10, true);滑块的两个文本参数只是标签,值要自己TextFormat出来再传。GuiSpinner的editMode传true表示"这一帧允许直接点输入框手打",不想要就传false。
让选项二选一或多选一
开关闭合用复选框,档位切换用下拉框。
bool paused = false; bool dropOpen = false; // 必须自己存,下一帧原样传回 int zone = 0; GuiCheckBox((Rectangle){ 40, 160, 22, 22 }, "暂停", &paused); GuiDropdownBox((Rectangle){ 40, 200, 160, 30 }, "简单;普通;困难", &zone, dropOpen);下拉框的editMode必须存进变量回传,传常量false的后果是菜单刚展开下一帧就被关掉。复选框返回true的那一帧就是状态改变瞬间,适合触发一次性动作;之后每帧都是 0,别拿它当持久状态读。
让长列表滚得动
条目超过一屏,就把列表装进滚动面板:
int rows = 8; Rectangle row = { 40, 40, 200, 30 }; Vector2 scroll = { 0 }; Rectangle view = { 0 }; GuiScrollPanel((Rectangle){ 40, 40, 200, 180 }, NULL, (Rectangle){ 0, 0, 200, (float)(rows * 35) }, &scroll, &view); BeginScissorMode((int)view.x, (int)view.y, (int)view.width, (int)view.height); for (int i = 0; i < rows; i++) { row.y = view.y + scroll.y + 5 + i * 35; char t[32]; snprintf(t, 32, "Item %d", i); GuiButton(row, t); } EndScissorMode();view是库算出来的可见窗口,BeginScissorMode/EndScissorMode负责裁掉视口外的行,这一步漏了控件会溢出到面板外面。滚动偏移归库管,你只管下一帧接着传&scroll,不用自己写滚轮监听。
面板之外的部分照旧是普通游戏绘制:可拖拽的控制点、曲线、菜单,全在一块画布上混排,互不打架。
官方示例拆解
examples/shapes/shapes_pie_chart.c 是个好标本:左边一张饼图,右边一整块 270 像素宽的控制面板。
面板里的代码很典型:
GuiSpinner((Rectangle){ panelPos.x + 95, (float)panelPos.y + 12, 125, 25 }, "Slices ", &sliceCount, 1, MAX_PIE_SLICES, false); GuiCheckBox((Rectangle){ panelPos.x + 20, (float)panelPos.y + 12 + 40, 20, 20 }, "Show Values", &showValues); GuiSliderBar((Rectangle){ panelPos.x + 80, (float)panelPos.y + 12 + 130, panelRect.width - 100, 30 }, "Inner Radius", NULL, &donutInnerRadius, 5.0f, radius - 10.0f);分界线很干脆:raygui 负责输入采集、控件外观、滚动视口(GuiScrollPanel);你负责把values[]画成饼、用atan2f算出鼠标悬停在第几片。所有Gui*()调用在右边,所有DrawCircleSector()和角度计算在左边。面板锚在右上角,x 坐标由screenWidth - panelMargin - panelWidth反推——窗口怎么拉,它都贴边。切片数一变,每行滑块就重新生成,所以values[]数组必须留在帧外存着,不然数据每帧清零。
常见问题排查表
| 现象 | 原因 | 解法 |
|---|---|---|
| 链接报重复符号(GuiSliderBar already defined) | RAYGUI_IMPLEMENTATION在多个.c里都定义了 | 只在其中一个定义,其余文件直接 include |
| 下拉框点一下就消失 | editMode传了常量false | 用bool变量存开合状态,每帧回传 |
| 中文控件文字显示成方块 | 内置默认字体不含 CJK 字形 | GuiSetFont(LoadFontEx("fonts/cjk.ttf", 22, 0, 256))换字体 |
| 文字溢出控件框 | 默认字号与控件高度不匹配 | 调大控件height,或用GuiSetStyle(DEFAULT, FONT_SIZE, ...)调小字号 |
| 点击事件只触发一次 | GuiCheckBox返回值只在状态改变那一帧为 true | 想读持久状态就取*checked,想做事就在那一帧处理 |
接入清单
git clone https://gitcode.com/GitHub_Trending/ra/raylib,raygui.h这个单文件头在 examples 的每个模块目录里都有一份,拷一份进你项目的src/。- 挑一个
.c文件,在#include "raygui.h"之前写#define RAYGUI_IMPLEMENTATION,其余源文件只写 include。 - 编译时链接 raylib,Linux 下一行搞定:
gcc demo.c -lraylib -lGL -lm -lpthread -ldl -o demo;Windows 与 Web 平台走 raylib 官方 CMake / Makefile,把raygui.h一起带上即可。
收尾
raygui 的哲学就一句:变量即真相——界面随时可以删了重写,只要每帧声明一遍,控件就永远跟数据同步。接下来值得玩的方向:用GuiSetStyle()调字号和配色,或GuiLoadStyle()直接加载.rgs样式文件换主题;再去 examples 里翻翻GuiTabBar、GuiColorPicker、GuiTextBox,看看别人怎么把整块屏幕铺满。
【免费下载链接】raylibA simple and easy-to-use library to enjoy videogames programming项目地址: https://gitcode.com/GitHub_Trending/ra/raylib
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考