用JSON定义游戏界面:FlatUI序列化功能完整上手教程
用JSON定义游戏界面:FlatUI序列化功能完整上手教程
【免费下载链接】flatuiEfficient Immediate Mode UI for Games项目地址: https://gitcode.com/gh_mirrors/flatu/flatui
FlatUI 是 Google 开源的高效即时模式(Immediate Mode)游戏 UI 库,主打轻量、快速、零状态管理的界面渲染方案。而它的序列化功能(Serialization)更是让开发者可以用 JSON 直接定义游戏界面,把界面布局与 C++ 代码彻底分离,堪称游戏开发者的"界面配置神器"。这篇教程将带你从零上手 FlatUI 序列化,学会用 JSON 快速搭建游戏菜单、配置控件、绑定事件,并扩展自定义控件,全程无需反复编译即可调整界面。
什么是 FlatUI 序列化功能
传统即时模式 UI 中,界面代码和逻辑代码混在一起,改一个按钮位置都要重新编译。FlatUI 序列化功能改变了这一切:它借助FlatBuffers序列化框架,允许你用一份 JSON 文件描述整个游戏界面(控件、布局、尺寸、事件),运行时再解析加载、渲染显示。
它的核心价值在于:
- 界面与代码解耦:改 UI 文案、颜色、布局只改 JSON,不动 C++ 代码;
- 天然支持多人协作:策划、美术可以直接编辑 JSON,程序员专注逻辑;
- 低开销高性能:FlatBuffers 序列化后的二进制数据无需解析即可直接读取,契合游戏对性能的苛刻要求;
- 支持动态数据与自定义控件:可注册运行时可变的变量,也可扩展专属控件。
FlatUI 序列化核心原理:JSON 到界面的一次旅行
整个流程可以概括为三步:写 JSON → 用 Schema 解析成二进制 → 运行时反序列化渲染。
负责这一切的骨架是 flatui.fbs(FlatBuffers Schema 文件),它定义了界面的"语法规则"。其中FlatUIElement是核心表结构,包含了id、type、layout、text、size、margin、offset、horizontal、vertical等字段;而Type枚举则列出了 FlatUI 内置支持的控件类型:
| 控件类型 | 用途 | 常用字段 |
|---|---|---|
Label | 文本标签 | text、ysize |
TextButton | 文本按钮 | text、size、margin |
Image | 图片显示 | texture、ysize |
ImageButton | 图片按钮 | texture、size |
Edit | 文本输入框 | ysize、size_2f |
Slider | 滑动条 | texture、size_2f、bar_size |
CheckBox | 复选框 | texture、text |
ScrollBar | 滚动条 | texture、size、bar_size |
Group | 容器分组 | layout、horizontal、vertical、offset |
SetVirtualResolution | 设置虚拟分辨率 | virtual_resolution |
关于 Schema 的完整字段定义,可以参考 flatui.fbs 中的注释说明。
快速上手:用 JSON 定义第一个游戏界面
我们直接看 FlatUI 自带的序列化示例 first_menu.json,它定义了一个居中的垂直布局菜单,包含标题、按钮和输入框:
{ elements: [ { type: "flatui_data.Type.SetVirtualResolution", id: "virtual resolution", virtual_resolution: 1000 }, { type: "flatui_data.Type.Group", id: "first menu group", layout: VerticalLeft, horizontal: Center, vertical: Center, elements: [ { type: "flatui_data.Type.Label", id: "first menu label", text: "Welcome to the first menu! :D", ysize: 40 }, { type: "flatui_data.Type.Edit", id: "first menu edit text", ysize: 40, size_2f: { x: 0, y: 0 } } ] } ] }这段 JSON 传达了几个关键信息:
- type指明控件类型,如
Label、Group、Edit; - id是控件的唯一标识,后续绑定事件、注册动态数据全靠它;
- layout / horizontal / vertical控制布局方向与对齐方式,
Center表示居中; - elements支持无限嵌套,实现树形界面结构,与 C++ 中的
StartGroup / EndGroup逻辑一一对应。
在 C++ 中加载并渲染 JSON 界面
JSON 写好了,接下来用 C++ 把它加载进游戏。完整的可运行示例在 flatuiserializationsample.cpp,核心流程如下:
- 加载文件:读取 JSON、自定义控件 Schema 和主 Schema;
- 解析生成二进制:用
flatbuffers::Parser结合 Schema 把 JSON 编译成 FlatBuffer 二进制数据; - 创建界面:调用
CreateFlatUIFromData()传入二进制数据; - 渲染循环:把创建函数传入
flatui::Run()每帧执行。
flatbuffers::Parser parser; parser.Parse(schema.c_str(), include_directories); // 解析 Schema parser.Parse(first_menu_json.c_str()); // 解析 JSON auto* data = parser.builder_.GetBufferPointer(); // 拿到二进制数据 Run(assetman, fontman, input, [&]() { flatui::CreateFlatUIFromData(data, &assetman, event_handler); });CreateFlatUIFromData是序列化 API 的核心入口,其完整签名定义在 flatui_serialization.h,接受三个参数:FlatBuffer 数据、用于纹理渲染的AssetManager(可选)、以及事件处理器FlatUIHandler(可选)。
事件绑定与动态数据:让界面"活"起来
静态界面没有意义,FlatUI 序列化通过事件绑定和动态数据让界面与游戏逻辑联动。
事件处理
事件处理器是一个std::function,接收事件、控件 ID 和动态数据三个参数。示例代码中通过 lambda 包装后传入CreateFlatUIFromData:
auto event_handler = & { EventHandler(e, id, dynamic_data, menu_id); };在EventHandler内部按控件id分发逻辑,例如监听"change menu button"的kEventWentUp(松开)事件切换菜单,实现两个菜单之间的来回切换。
动态数据注册
输入框里的文字是运行时可变的,需要用RegisterStringData把它和 JSON 中的控件 ID 绑定:
std::string edit_text_box("Edit me!"); flatui::RegisterStringData("first menu edit text", &edit_text_box);FlatUI 提供了一套完整的注册函数:RegisterIntData、RegisterFloatData、RegisterBoolData、RegisterVec2Data、RegisterVec4Data等,对应DynamicData联合体中的各种数据类型,全部声明在 flatui_serialization.h。注意:注册的指针必须比界面的生命周期更长,否则会引发悬垂指针。
自定义控件:扩展 FlatUI 的专属武器
内置控件不够用时,FlatUI 序列化还支持自定义控件。步骤很简单:
- 定义枚举:在自定义 Schema(如 custom_widgets.fbs)中声明控件类型;
- 实现渲染函数:编写一个符合
CustomWidget签名的函数,用element中的数据调用 FlatUI 绘制 API; - 注册控件:调用
RegisterCustomWidget(type, widget)把类型与函数绑定。
void ChangeMenuButton(const flatui_data::FlatUIElement* element, fplbase::AssetManager* assetman, flatui::FlatUIHandler event_handler, flatui::DynamicData* dynamic_data) { flatui::StartGroup(flatui::kLayoutVerticalLeft); flatui::ColorBackground(fplbase::LoadVec4(...)); flatui::Event e = flatui::TextButton(element->text()->c_str(), element->size()); flatui::EndGroup(); event_handler(e, element->id()->str(), dynamic_data); } flatui::RegisterCustomWidget(custom_widgets::Type_ChangeMenuButton, ChangeMenuButton);自定义控件还能通过attributes字段接收 JSON 中传入的任意参数(颜色、尺寸、对齐方式等),灵活性极高,示例中的"Change Menu"按钮就是典型应用。
调试技巧与常见问题
JSON 写错了怎么办?FlatUI 内置了错误输出机制,默认最多打印 10 条错误,可通过SetErrorOutputCount()调整:
flatui::SetErrorOutputCount(flatui::kNoErrorOutputLimit); // 输出所有错误常见坑位提醒:
- JSON 格式不合法:示例代码在
Parse失败时会直接报错退出,务必检查括号与逗号; - texture 找不到:渲染图片控件时,确保
AssetManager中已加载对应纹理资源; - id 冲突或遗漏:事件绑定和动态数据都依赖
id,命名要全局唯一; - Schema 版本不一致:JSON 字段必须与
flatui.fbs版本匹配,否则解析报错。
总结:什么时候该用 FlatUI 序列化
如果你正在开发游戏,且满足以下任一场景,强烈建议尝试 FlatUI 序列化功能:
- 需要频繁调整界面布局,希望免编译热改 UI;
- 团队中有策划或美术参与界面配置;
- 需要动态生成菜单、弹窗等结构化界面;
- 追求极低开销的序列化方案(FlatBuffers 天然零拷贝)。
上手方式也很简单:将仓库https://gitcode.com/gh_mirrors/flatu/flatui克隆到本地,直接运行sample/serialization目录下的示例工程,对照 first_menu.json、second_menu.json 和 flatuiserializationsample.cpp 边改边看,很快就能掌握用 JSON 定义游戏界面的全部技巧!
【免费下载链接】flatuiEfficient Immediate Mode UI for Games项目地址: https://gitcode.com/gh_mirrors/flatu/flatui
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
