YimMenu Lua 脚本指南:tab 类完整解析与 GUI 界面构建实战
【免费下载链接】YimMenuYimMenu, a GTA V menu protecting against a wide ranges of the public crashes and improving the overall experience.项目地址: https://gitcode.com/GitHub_Trending/yi/YimMenu
tab 是 YimMenu 的 Lua API 中用于表示菜单 GUI 标签页的核心类,脚本开发者通过它可以在游戏菜单中动态创建顶层标签页与子标签页,并向其中填充按钮、文本、复选框、输入框等各类控件。本文以 docs/lua/classes/tab.md 为主线,结合 src/lua/bindings/gui.cpp 等源码实现,系统讲解 tab 类的全部 12 个方法及其底层原理,帮助读者掌握用 Lua 为 YimMenu 构建自定义界面的完整技能。
一、tab 类是什么:一个"代表 GUI 标签页"的对象
在 YimMenu 中,整个菜单界面由一个个"标签页(tab)"组成,例如玩家、自我、载具、武器等一级导航,以及其下层层嵌套的子标签页。tab 类就是这一概念在 Lua 脚本层的抽象:每个 tab 实例对应 GUI 中的一个标签页节点,脚本可以调用它的方法在对应位置添加控件,或继续创建子标签页。
从源码看,tab 类的 C++ 定义位于 src/lua/bindings/gui.hpp,内部维护两个关键状态:
big::tabs m_id:标签页在 GUI 导航树中的唯一 ID;rage::joaat_t m_tab_hash:标签页名称经 joaat 哈希后得到的标识符,用于在导航树中定位节点。
构造函数有两个重载:tab(name)创建顶层标签页,tab(name, parent_tab_hash)创建挂在指定父标签页下的子标签页(gui.cpp)。创建时会先通过check_if_existing_tab_and_fill_id遍历导航树,若同名标签页已存在则直接复用其 ID;否则调用make_tab_nav分配一个RUNTIME_CUSTOM起始的自定义 ID 并插入导航树(gui_service.hpp)。
脚本中通常不直接构造 tab,而是通过gui.get_tab(name)或gui.add_tab(name)获取实例,详见 docs/lua/tables/gui.md。
二、获取与创建标签页:gui 表与 add_tab 方法
在 Lua 脚本中拿到 tab 实例有两种途径:
-- 获取已存在的标签页(不存在时也会自动创建) tab = gui.get_tab("MyTab") -- 直接新建一个顶层标签页 tab = gui.add_tab("MyTab")底层两者都会构造tab(name),并执行上述"查找已存在 / 插入导航树"的逻辑(gui.cpp)。
拿到顶层 tab 后,就可以用本文的主角tab:add_tab(tab_name)创建它的子标签页:
sub_tab = tab:add_tab("Sub Menu")- 参数:
tab_name(string)—— 子标签页的名称; - 返回:
tab—— 新子标签页的实例,可继续对其调用本文其余方法。
子标签页同样会先尝试复用同名已存在节点,新建时通过add_to_existing_tab将新节点挂到父标签页的sub_nav子导航映射下,并把父子关系记录进模块的m_tab_to_sub_tabs,同时把新标签页 ID 登记进m_owned_tabs以便后续清理(gui.cpp、lua_module.hpp)。因此可以无限层层嵌套,构造任意深度的菜单树。
三、tab 的查询与清理:is_selected 与 clear
is_selected():判断当前是否处于该标签页
boolean = tab:is_selected()- 返回:
boolean—— 若该标签页当前正被选中则返回 true。
源码实现只有一行核心判断:g_gui_service->get_selected()->hash == m_tab_hash,即比较当前选中导航项的 hash 与该 tab 的 hash(gui.cpp)。典型的用途是配合每帧回调实现"仅当用户停留在本标签页时才更新界面数据",减少不必要的开销。
clear():清空本标签页的自定义内容
tab:clear()clear会清除当前 Lua 模块挂在该标签页 hash 下的全部自定义元素(m_gui[m_tab_hash].clear()),并遍历m_tab_to_sub_tabs[id()]中所有由本模块创建的子标签页,若属于m_owned_tabs则调用g_gui_service->remove_from_nav将其从导航树中移除(gui.cpp)。
两点需要特别注意:
- 它只清理自己这个 Lua 模块添加的内容(通过
sol::state_view(state)["!this"]获取当前模块实例),不会误删其他脚本或菜单自带的内容; - 它会连同该标签页下由本模块创建的子标签页一起移除,因此常用于脚本重载、重置界面布局等场景。
四、基础控件:add_button 与 add_text
add_button(name, callback):添加按钮
tab:add_button("Do Something", function() -- 点击按钮后执行的逻辑 end)- 参数:
name(string)—— 按钮上显示的文字;callback(function)—— 点击按钮时被调用的函数。
按钮的实现类lua::gui::button(src/lua/bindings/gui/button.cpp)在draw()中调用ImGui::Button(m_text.data()),点击后通过big::g_fiber_pool->queue_job将回调投递到游戏 fiber 池中执行,这样回调里就可以安全调用游戏 natives;若回调执行出错,则交给g_lua_manager->handle_error统一处理,避免脚本崩溃。
add_text(name):添加文本
text = tab:add_text("Hello, YimMenu!")- 参数:
name(string)—— 要显示的文字; - 返回:
text—— 文本对象实例,可用于后续修改。
text 对象在绑定层暴露了get_text/set_text/set_font三个方法(gui.cpp),详见 docs/lua/classes/base_text_element.md。想了解更丰富的文本用法可参考 docs/lua/classes/text.md。
五、状态控件:add_checkbox
checkbox = tab:add_checkbox("Enable Feature") checkbox:set_enabled(true) -- 默认勾选 local state = checkbox:is_enabled() -- 读取状态- 参数:
name(string)—— 显示在复选框旁的说明文字; - 返回:
checkbox—— 复选框对象实例。
复选框封装了ImGui::Checkbox(src/lua/bindings/gui/checkbox.cpp),并通过 Lua 绑定暴露get_text/set_text/is_enabled/set_enabled(gui.cpp)。实践中常配合循环命令或每帧回调实现功能开关:
local cb = tab:add_checkbox("God Mode") script.register_looped("my_script", function(script) if cb:is_enabled() then -- 每帧执行的功能逻辑 end end)六、布局控件:add_sameline 与 add_separator
这两个方法用于控制控件在标签页内的排列方式,都直接映射到 ImGui 原生 API:
tab:add_text("Name:") sameline = tab:add_sameline() -- 等价于 ImGui::SameLine() tab:add_input_string("") -- 输入框紧跟在上一控件右侧add_sameline():添加一个ImGui::SameLine,使下一个控件与当前行最后一个控件并排显示,常用于"标签 + 输入框"、"按钮 + 按钮"等紧凑布局;add_separator():添加一个ImGui::Separator,即一条水平分隔线,用于把标签页内容划分成视觉分区。
两者均返回对应的对象实例(sameline/separator),绑定层以空 usertype 暴露(gui.cpp),用法见 docs/lua/classes/sameline.md 与 docs/lua/classes/separator.md。
七、输入控件:add_input_int、add_input_float 与 add_input_string
三个输入控件分别封装 ImGui 的InputInt、InputFloat、InputText,签名一致:
input_int = tab:add_input_int("Count") -- 整型输入 input_float = tab:add_input_float("Scale") -- 浮点输入 input_string = tab:add_input_string("Message") -- 文本输入- 参数:
name(string)—— 显示在输入框旁的说明文字; - 返回:对应输入对象实例,均可调用
get_text/set_text修改标签文字,以及get_value/set_value读写当前值(gui.cpp)。
以input_int为例,其draw()实现为ImGui::InputInt(m_text.c_str(), &m_value),get_value/set_value直接读写内部m_value(src/lua/bindings/gui/input_int.cpp)。因此即使玩家没有手动输入,脚本也可以在绘制前用set_value预设默认值:
local amount = tab:add_input_int("Money Amount") amount:set_value(1000) -- 预设默认值 local pos_x = tab:add_input_float("X") pos_x:set_value(0.0)各控件的详细说明见 docs/lua/classes/input_int.md、docs/lua/classes/input_float.md 与 docs/lua/classes/input_string.md。
八、高级能力:add_imgui 直接渲染 ImGui
当内置控件无法满足需求时,可以用add_imgui注册一个每帧都会执行的回调函数,在回调中直接调用任何 ImGui API:
tab:add_imgui(function() if ImGui.Begin("My Custom Window") then if ImGui.Button("Label") then script.run_in_fiber(function(script) -- 在这里调用游戏 natives end) end ImGui.End() end end)- 参数:
imgui_rendering(function)—— 每帧渲染时被调用的函数。
两点重要提醒:
- 回调每帧在渲染线程执行,不能直接调用游戏 natives。需要调用 natives 时,务必像示例那样用
script.run_in_fiber把逻辑投递到 fiber 中执行; - 该回调只在标签页所属模块渲染时被绘制,与全局的
gui.add_imgui、gui.add_always_draw_imgui(后者在菜单关闭时也执行)作用域不同,后两者详见 docs/lua/tables/gui.md。
完整的 ImGui API 列表请查阅 docs/lua/tables/ImGui.md,更复杂的渲染场景可参考 docs/lua/classes/raw_imgui_callback.md。
九、综合示例:构建一个完整的自定义标签页
结合以上全部方法,一个典型的 YimMenu Lua 脚本界面如下:
-- 创建顶层标签页和子标签页 local main_tab = gui.add_tab("My Script") local settings = main_tab:add_tab("Settings") -- 在 Settings 子页添加控件 settings:add_text("=== Options ===") settings:add_separator() local enable = settings:add_checkbox("Enable Feature") local amount = settings:add_input_int("Amount") amount:set_value(1) settings:add_sameline() settings:add_button("Apply", function() if not enable:is_enabled() then gui.show_warning("My Script", "Feature is disabled") return end script.run_in_fiber(function(script) -- 在这里调用 natives 执行功能 end) end) -- 每帧刷新:仅当本页被选中时才刷新输入框默认值 script.register_looped("my_script", function(script) if main_tab:is_selected() then -- 刷新界面相关状态 end end)示例中gui.show_warning用于弹出通知(实现见 gui.cpp),script.run_in_fiber与script.register_looped的用法可参考 docs/lua/tables/script.md。
十、方法与底层实现对照表
下表汇总 tab 类的全部 12 个方法及对应源码位置,便于进一步研读:
| Lua 方法 | 参数 | 返回值 | 底层 ImGui 控件 | 源码实现 |
|---|---|---|---|---|
is_selected() | 无 | boolean | — | gui.cpp |
clear() | 无 | 无 | — | gui.cpp |
add_tab(name) | string | tab | 导航树节点 | gui.cpp |
add_button(name, cb) | string, function | button | ImGui::Button | gui.cpp |
add_text(name) | string | text | ImGui::Text系 | gui.cpp |
add_checkbox(name) | string | checkbox | ImGui::Checkbox | gui.cpp |
add_sameline() | 无 | sameline | ImGui::SameLine | gui.cpp |
add_separator() | 无 | separator | ImGui::Separator | gui.cpp |
add_input_int(name) | string | input_int | ImGui::InputInt | gui.cpp |
add_input_float(name) | string | input_float | ImGui::InputFloat | gui.cpp |
add_input_string(name) | string | input_string | ImGui::InputText | gui.cpp |
add_imgui(callback) | function | raw_imgui_callback | 直接调用 ImGui | gui.cpp |
所有方法通过tab_ut(sol2 usertype)注册到 Lua 运行时,注册代码集中在 gui.cpp。控件全部实现gui_element接口的draw()虚方法(src/lua/bindings/gui/gui_element.hpp),由 GUI 服务在渲染时统一调用,这也是"每帧重绘"模式的根基。更完整的 Lua 编程入口可参考 docs/lua/commands.md 与 docs/lua/tabs.md。
【免费下载链接】YimMenuYimMenu, a GTA V menu protecting against a wide ranges of the public crashes and improving the overall experience.项目地址: https://gitcode.com/GitHub_Trending/yi/YimMenu
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考