YimMenu Lua 脚本指南:tab 类完整解析与 GUI 界面构建实战
2026/9/18 8:30:22 网站建设 项目流程

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)。

两点需要特别注意:

  1. 它只清理自己这个 Lua 模块添加的内容(通过sol::state_view(state)["!this"]获取当前模块实例),不会误删其他脚本或菜单自带的内容;
  2. 它会连同该标签页下由本模块创建的子标签页一起移除,因此常用于脚本重载、重置界面布局等场景。

四、基础控件: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 的InputIntInputFloatInputText,签名一致:

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)—— 每帧渲染时被调用的函数。

两点重要提醒:

  1. 回调每帧在渲染线程执行,不能直接调用游戏 natives。需要调用 natives 时,务必像示例那样用script.run_in_fiber把逻辑投递到 fiber 中执行;
  2. 该回调只在标签页所属模块渲染时被绘制,与全局的gui.add_imguigui.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_fiberscript.register_looped的用法可参考 docs/lua/tables/script.md。

十、方法与底层实现对照表

下表汇总 tab 类的全部 12 个方法及对应源码位置,便于进一步研读:

Lua 方法参数返回值底层 ImGui 控件源码实现
is_selected()booleangui.cpp
clear()gui.cpp
add_tab(name)stringtab导航树节点gui.cpp
add_button(name, cb)string, functionbuttonImGui::Buttongui.cpp
add_text(name)stringtextImGui::Textgui.cpp
add_checkbox(name)stringcheckboxImGui::Checkboxgui.cpp
add_sameline()samelineImGui::SameLinegui.cpp
add_separator()separatorImGui::Separatorgui.cpp
add_input_int(name)stringinput_intImGui::InputIntgui.cpp
add_input_float(name)stringinput_floatImGui::InputFloatgui.cpp
add_input_string(name)stringinput_stringImGui::InputTextgui.cpp
add_imgui(callback)functionraw_imgui_callback直接调用 ImGuigui.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),仅供参考

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

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

立即咨询