DMUI 4 · API 参考
DMUI 4 运行在 Roblox 客户端执行器上。所有对外能力通过 DMRIScriptCenter.API 下的 BindableEvent 暴露。
- 大部分创建类走异步 callback,但
创建Main左Tab没有 callback,需手动等待; - 几乎所有 Fire 都无返回值,唯独
定位.新的定位同步返回BillboardGui; - 框架不提供 UI 就绪事件,所有创建后等待依赖
task.wait或轮询。
概览
完整 API 目录树
以下是从 DMRIScriptCenter.API 展开的完整 BindableEvent 树。键名区分大小写,写错任何一层都会 attempt to index nil。
DMRIScriptCenter.API
├── 创建
│ ├── Main
│ │ ├── 创建Main左Tab (BindableEvent) -- 无 callback
│ │ └── 创建内容板块
│ │ └── 创建内容板块1 (BindableEvent)
│ ├── 窗口
│ │ ├── 创建模板窗口 (BindableEvent)
│ │ ├── 飞行窗口 (BindableEvent)
│ │ ├── 玩家选择窗口 (BindableEvent)
│ │ ├── 物品选择窗口 (BindableEvent)
│ │ ├── 实时定位 (BindableEvent)
│ │ ├── 历史通知 (BindableEvent)
│ │ ├── 查看虚拟形象 (BindableEvent)
│ │ └── 玩家档案 (BindableEvent)
│ └── 实时列表 (BindableEvent)
│
├── 功能
│ ├── 通知 (BindableEvent)
│ ├── 删除通知历史 (BindableEvent)
│ ├── 菜单
│ │ ├── 弹出菜单 (BindableEvent)
│ │ └── 取消绑定 (BindableEvent)
│ ├── 飞行 (BindableEvent)
│ ├── 观战 (BindableEvent)
│ ├── 定位
│ │ ├── 新的定位 (BindableEvent) -- ★ 同步返回 BillboardGui
│ │ └── 删除定位 (BindableEvent)
│ ├── 窗口缩放
│ │ ├── 设置缩放 (BindableEvent)
│ │ ├── 归一化尺寸 (BindableEvent)
│ │ ├── 全屏切换 (BindableEvent)
│ │ ├── 清理缩放 (BindableEvent)
│ │ └── 设置手柄图标 (BindableEvent)
│ ├── 镜头追踪
│ │ ├── 应用跟随鼠标 (BindableEvent)
│ │ └── 取消跟随鼠标 (BindableEvent)
│ └── 跟随鼠标
│ ├── 应用跟随鼠标 (BindableEvent)
│ └── 取消跟随鼠标 (BindableEvent)
│
└── 数据
├── 游戏信息
│ └── 请求
│ ├── 游戏名字 (BindableEvent)
│ ├── 游戏icon (BindableEvent)
│ ├── 游戏ID (BindableEvent)
│ ├── 游戏开发者 (BindableEvent)
│ ├── 游戏描述 (BindableEvent)
│ ├── 服务器ID (BindableEvent)
│ └── 游戏缩略图 (BindableEvent)
└── 时间信息
└── 请求
├── 日期(中国) (BindableEvent)
├── 日期 (BindableEvent)
├── 招呼(中国) (BindableEvent)
├── 招呼 (BindableEvent)
├── 时间(H) (BindableEvent)
├── 时间(M) (BindableEvent)
├── 时间(S) (BindableEvent)
├── 时间(Y) (BindableEvent)
├── 时间(Mon) (BindableEvent)
├── 时间(D) (BindableEvent)
└── 时间(W) (BindableEvent)
assert,否则路径写错只会在运行时得到不清晰的 nil 索引错误:
local function getAPI(path)
local node = APIFold
for key in path:gmatch("[^%.]+") do
node = node and node:FindFirstChild(key)
end
assert(node and node:IsA("BindableEvent"),
"[DMUI4] 无效 API 路径: " .. path)
return node
end
getAPI("创建.窗口.创建模板窗口"):Fire(...)
三类调用范式(含同步 / 异步标注)
| 范式 | 典型写法 | 结果获取 | 代表 API |
|---|---|---|---|
| 创建类·异步 callback | Fire(参数..., { callback = fn }) | 异步回调 fn(实例) | 创建内容板块 / 创建模板窗口 / 选择窗口 / 通知 / 菜单 |
| 创建类·无 callback(唯一例外) | Fire(Name, Icon) | 无返回,事后 screen[Name] 取 | 创建Main左Tab |
| 同步返回(唯一例外) | local x = Fire(Object) | 直接 return 实例 | 定位.新的定位 |
| 命令类·无返回 | Fire(action, options) / Fire({mods=...,...}) | 无返回;查询走 options.callback | 飞行 / 观战 / 窗口缩放 / 数据类 |
定位根目录
每个插件脚本开头应先定位根目录。DMUI 可能挂在 PlayerGui,也可能在 CoreGui(取决于执行器)。
local Player = game.Players.LocalPlayer
local FoldAll = Player.PlayerGui:FindFirstChild("DMRIScriptCenter")
or game:GetService("CoreGui"):FindFirstChild("DMRIScriptCenter")
if not FoldAll then error("DMUI 4 未加载") end
local APIFold = FoldAll.API
local configurationFiles = FoldAll["配置文件"]
local screen = FoldAll.MainGui.Main.Content.screen
local primary = FoldAll.MainGui.Main.Content.navigation.directory.primary
configurationFiles
普通 Folder,直接读写 .Value 即可。所有 UI 组件都监听对应项的变化并实时重绘。
| 子项 | 类型 | 取值 / 默认 |
|---|---|---|
| 版本 | StringValue | "DMUI 4" |
| Build号 | StringValue | "Build 6" |
| 创建时间 | StringValue | 构建日期字符串 |
| 语言 | StringValue | zh-cn / zh-tw / en / jp / es-es / pt-br |
| 通知位置 | StringValue | 左 / 中 / 右 |
| 实时列表位置 | StringValue | 左 / 右 |
| 主颜色 | Color3Value | Color3.fromRGB(23,147,255) |
| 主菜单状态 | IntValue | 1 双栏 / 2 只内容 / 3 只导航 |
| 通知设置/保存最大值 | IntValue | 5–50,默认 30 |
| Spectate设置/菜单方向 | StringValue | 上 / 下 |
创建类 API
统一约定:Fire(参数..., { callback = function(result) ... end })。Fire 本身无返回值,结果一律通过 callback 异步返回。
在主窗口创建一个带侧栏导航的 Tab。
| 参数 | 类型 | 说明 |
|---|---|---|
| BuildName | string | Tab 名称,同时作为 screen[BuildName] 与 primary[BuildName] 的 key |
| IconImage | string | 图标资源 id,如 "rbxassetid://105606186410454" |
返回值 / Callback
本 API 无 callback 参数。创建后通过 screen[BuildName] 与 primary[BuildName] 取实例。这是框架中唯一不支持 callback 的创建 API。
APIFold["创建"].Main["创建Main左Tab"]:Fire(
"设置", "rbxassetid://105606186410454"
)
task.wait(2) -- 必须等待 UI 就绪
local SetMain = screen["设置"]
local scroll = SetMain.scroll
task.wait。UI 就绪等待(推荐封装)
创建Main左Tab 没有 callback,框架也不提供就绪事件。硬编码 task.wait(2) 在低端设备上可能过短,在高端设备上浪费 2 秒。推荐使用轮询 + 超时封装:
local function WaitForTab(name, timeout)
local deadline = os.clock() + (timeout or 10)
repeat
task.wait(0.05)
local node = screen:FindFirstChild(name)
local nav = primary:FindFirstChild(name)
if node and nav and node:FindFirstChild("scroll") then
return node, nav
end
until os.clock() > deadline
error(("[DMUI4] 等待 Tab '%s' 超时"):format(name))
end
local function WaitForChild(parent, name, timeout)
local deadline = os.clock() + (timeout or 10)
repeat
task.wait(0.05)
local c = parent and parent:FindFirstChild(name)
if c then return c end
until os.clock() > deadline
error(("[DMUI4] 等待子节点 '%s' 超时"):format(name))
end
-- 用法
APIFold["创建"].Main["创建Main左Tab"]:Fire("设置", "rbxassetid://105606186410454")
local SetMain, LeftSetMain = WaitForTab("设置")
local scroll = SetMain.scroll
在指定 ScrollingFrame 中创建一个带头部图标的内容板块。
| 参数 | 类型 | 说明 |
|---|---|---|
| TaskName | string | 板块标题 |
| Image | string | 板块图标资源 id |
| Parent | ScrollingFrame | 通常传上一步的 scroll |
| LayoutOrder | number | 在同父级中的排序 |
| Data.Callback | function | 接收 pinned 实例 |
lable:框架内部标题节点命名确实是 lable(拼写错误),不是文档笔误。访问标题文本时必须写 lable,写 label 会得到 nil。
Callback 返回格式 · pinned 结构
pinned (Frame)
├── Name = TaskName
├── Size = UDim2.new(1, 0, 0, 0)
├── AutomaticSize = Enum.AutomaticSize.Y
├── header (Frame)
│ └── header.header (Frame) -- 头部胶囊
│ ├── icon (ImageLabel) -- 图标
│ └── lable (TextLabel) -- 标题文本(★ 拼写是 lable)
└── content (Frame)
└── 开发者的按钮 / 控件
local Pinned
APIFold["创建"].Main["创建内容板块"]["创建内容板块1"]:Fire(
"置顶功能", "rbxassetid://103234350480128", scroll, 3,
{ callback = function(pinned)
-- pinned 是 Frame
print(pinned.Name) -- "置顶功能"
print(pinned.header.header.lable.Text) -- "置顶功能"(注意 lable 拼写)
local btn = Instance.new("TextButton", pinned.content)
btn.Size = UDim2.new(0, 120, 0, 40)
btn.Text = "点我"
end }
)
创建一个 Leaflet 风格的模板窗口。窗口自带拖拽、右下角缩放手柄、最小化到任务栏、关闭按钮、层级焦点管理。
| 参数 | 类型 | 说明 |
|---|---|---|
| Title | string | 窗口标题 |
| MinX / MinY | number | 最小内容尺寸(框架会加 150px 总边距) |
| MaxX / MaxY | number | 最大内容尺寸(同样加 150px) |
| BuildX / BuildY | number | 入场动画起始偏移,通常传鼠标位置 |
| CanMini | boolean | 是否显示最小化按钮 |
| IconId | string | 最小化后任务栏图标 |
| Scale | number | 图标缩放 |
| Data.Callback | function | 接收 WinUI 实例 |
尺寸与边界行为
| 问题 | 实际行为 |
|---|---|
| MinX / MaxX 是最终尺寸吗? | 不是。它们描述「内容区尺寸」,框架在四周各加 75px 边距。最终窗口宽度 = MinX + 150。示例传 300,最小总宽是 450。 |
BuildX/BuildY 传 0,0 | 合法。入场从屏幕左上角播到中央。 |
BuildX/BuildY 传 nil | 等价 0,0,不报错。 |
| CanMini=false | win.frame.LeafletControls.Top.Small 节点不存在(不是隐藏)。访问得到 nil。 |
| MinX/MinY 与 MaxX/MaxY 相等 | 视为固定尺寸,右下角缩放手柄自动隐藏。 |
Callback 返回格式 · WinUI 完整层级树
WinUI (ImageLabel) ← 回调返回的对象
├── Name = Title
├── AnchorPoint = Vector2.new(0.5, 0.5)
├── Position = UDim2.new(0.5, 0, 0.5, 0)
├── Image = "rbxassetid://72548733587158"
├── ZIndex = 2 (聚焦) / 1 (失焦)
│
└── frame (ImageButton) ← 内容容器
├── Active = true
├── BackgroundColor3 = Color3.fromRGB(34, 34, 34)
├── Size = UDim2.new(1, 0, 1, 0)
│
├── Space (Frame) ← 焦点捕捉层
│
└── LeafletControls (Frame) ← 控制层(ZIndex = 99999)
├── Top (ImageButton) ← 标题栏(可拖拽)
│ ├── space (Frame) ← 弹性填充
│ ├── Title (ImageLabel)
│ │ └── Container (TextButton) ← 标题文本
│ ├── Close (ImageLabel)
│ │ └── Button (ImageButton) ← 关闭按钮
│ └── Small (ImageLabel) ← CanMini=true 时才存在
│ └── Button (ImageButton) ← 最小化按钮
│
└── Bottom (Frame) ← 右下角缩放手柄区
└── Resize (ImageButton) ← 缩放手柄(已自动绑定)
常用属性路径速查
| 路径 | 类型 | 可写属性 / 说明 |
|---|---|---|
| win | ImageLabel | Size / Position / ZIndex / ImageTransparency |
| win.frame | ImageButton | BackgroundColor3 / Size / ClipsDescendants / Active |
| win.frame.LeafletControls.Top.Title.Container | TextButton | Text(标题文本)/ TextSize / TextColor3 / FontFace |
| win.frame.LeafletControls.Top.Close.Button | ImageButton | 关闭按钮,已内置关闭动画 |
| win.frame.LeafletControls.Top.Small.Button | ImageButton | 最小化按钮(仅 CanMini=true) |
| win.frame.LeafletControls.Bottom.Resize | ImageButton | 缩放手柄(已自动绑定) |
| win.frame.Space | Frame | 焦点层,点击置顶 |
BuildX/Y 只决定动画起点,最终位置固定在屏幕中央。开发者内容应添加到 win.frame 下(不要加到 WinUI 本身,避免被 UIPadding 影响)。防御写法(CanMini=false)
local smallWrap = win.frame.LeafletControls.Top:FindFirstChild("Small")
if smallWrap and smallWrap:FindFirstChild("Button") then
smallWrap.Button.MouseButton1Click:Connect(function()
print("最小化被点击")
end)
end
local UIS = game:GetService("UserInputService")
APIFold["创建"]["窗口"]["创建模板窗口"]:Fire(
"我的窗口", 300, 200, 600, 500,
UIS:GetMouseLocation().X, UIS:GetMouseLocation().Y,
true, "rbxassetid://128067802960459", 0.8,
{ callback = function(win)
-- 1. 修改标题
win.frame.LeafletControls.Top.Title.Container.Text = "Hello DMUI"
-- 2. 添加内容
local lbl = Instance.new("TextLabel", win.frame)
lbl.Size = UDim2.new(1, 0, 1, 0)
lbl.BackgroundTransparency = 1
lbl.Text = "内容区"
lbl.TextColor3 = Color3.new(1, 1, 1)
lbl.TextScaled = true
-- 3. 监听关闭
win.frame.LeafletControls.Top.Close.Button.MouseButton1Click:Connect(function()
print("窗口关闭前执行")
end)
end }
)
给指定 ScrollingFrame 加一个可拖动的滚动指示器。
ScrollingFrame 必须设置 AutomaticCanvasSize = Enum.AutomaticSize.Y,否则指示器静默不显示(不抛错)。if scroll.AutomaticCanvasSize ~= Enum.AutomaticSize.Y then
scroll.AutomaticCanvasSize = Enum.AutomaticSize.Y
end
APIFold["创建"]["实时列表"]:Fire(scroll)
功能类 API
统一约定:Fire(Action: string, Options: table)。查询类通过 Options.callback 返回。
创建通知。支持 Notice(临时)、Keep(常驻,需续期),以及基于 noticeId 的查询 / 续期 / 关闭。
| 字段 | 类型 | 说明 |
|---|---|---|
| mods | string | Notice / Keep / GetKeepTime / GetKeepTimeLeft / DelKeep |
| icon | string[]? | 图标资源 id 列表 |
| content | table | function? | UI 规格表或构建函数 |
| input | {Text, TextColor}[]? | 仅 Notice,按钮列表 |
| time | number? | 仅 Notice,clamp 到 [5, 30] |
| callback | function? | 用户选择结果 / 超时为 nil |
| noticeId | number? | 查询 / 续期 / 关闭目标 ID |
| onCreate | function? | 创建成功后收到分配的数字 ID |
Content 规格表格式
每个元素形如 { ClassName = "…", 属性 = … , Children = {…} }。支持三种写法:
-- ① 单元素
content = {
ClassName = "TextLabel",
Text = "Hello",
TextSize = 18,
BackgroundTransparency = 1
}
-- ② 数组(多个元素,顺序稳定)
content = {
{ ClassName = "TextLabel", Text = "标题", TextSize = 18 },
{ ClassName = "TextLabel", Text = "副标题", TextSize = 14 }
}
-- ③ 字典(按 ClassName 分组,顺序不稳定)
content = {
["TextLabel"] = { Text = "Hello", TextSize = 18 },
["Frame"] = { Size = UDim2.new(0, 100, 0, 100) }
}
-- ★ Children 嵌套在三种写法下均生效
content = {
ClassName = "Frame",
Size = UDim2.new(0, 200, 0, 100),
Children = {
{ ClassName = "TextLabel", Text = "内嵌文本", Size = UDim2.new(1, 0, 1, 0) }
}
}
Callback 返回格式
| 情况 | callback 收到 |
|---|---|
| 用户点击了某个 input 按钮 | string(该按钮的 Text) |
| 超时 / 用户手动关闭 / 点击空白处 | nil |
| onCreate(创建成功时) | number(分配的通知数字 ID) |
边界与异常行为
| 场景 | 实际行为 |
|---|---|
| 多次创建 Keep | 每次分配新数字 ID,多个 Keep 独立存在。 |
DelKeep 传入已销毁的 noticeId | 静默返回 false,不抛错。 |
GetKeepTime 传入已销毁的 noticeId | 静默返回 nil,不抛错。 |
Keep 模式传 input | 静默忽略。Keep 不支持交互按钮。 |
| Keep 自定义超时时间 | 不支持。固定 300 秒,必须靠 GetKeepTime 续期。 |
content 传 nil | 创建空通知(只有 icon 与 input),不报错。 |
callback 传 nil | 合法,用户选择结果被丢弃。 |
APIFold["功能"]["通知"]:Fire({
mods = "Notice",
icon = { "rbxassetid://99589396911042" },
content = {
{ ClassName = "TextLabel",
Text = "确定要执行吗?",
TextSize = 18,
BackgroundTransparency = 1,
AutomaticSize = Enum.AutomaticSize.XY,
TextColor3 = Color3.new(1, 1, 1) }
},
input = {
{ Text = "确定", TextColor = Color3.fromRGB(100, 220, 120) },
{ Text = "取消", TextColor = Color3.fromRGB(220, 120, 120) },
},
time = 8,
callback = function(result)
-- result = "确定" / "取消" / nil
print("用户选择:", result)
end,
onCreate = function(id)
print("通知 ID:", id)
end,
})
local keepId
APIFold["功能"]["通知"]:Fire({
mods = "Keep",
icon = { "rbxassetid://99589396911042" },
content = {
{ ClassName = "TextLabel",
Text = "任务进行中...",
TextSize = 18,
BackgroundTransparency = 1,
AutomaticSize = Enum.AutomaticSize.XY,
TextColor3 = Color3.new(1, 1, 1) }
},
onCreate = function(id) keepId = id end,
})
-- 每 4 分钟续期
task.spawn(function()
while keepId do
task.wait(240)
APIFold["功能"]["通知"]:Fire({
mods = "GetKeepTime",
noticeId = keepId,
})
end
end)
-- 主动关闭
APIFold["功能"]["通知"]:Fire({
mods = "DelKeep",
noticeId = keepId,
})
统一飞行控制。Action 取值见下表。
| Action | 说明 |
|---|---|
| standard | 标准飞行(键盘 WASD/QE + 移动端摇杆) |
| vehicle | 载具飞行(更高速度倍率) |
| cframe | CFrame 飞行 |
| mobile | 移动端摇杆飞行 |
| toggle | 切换当前模式开/关 |
| setspeed | 只改速度不切换 |
| off | 关闭所有飞行 |
| isFlying | 查询,callback 返回 boolean |
| getMode | 查询,callback 返回当前模式 string |
Callback 返回格式
| Action | callback 收到 |
|---|---|
| isFlying | boolean(true = 飞行中,false = 未飞) |
| getMode | string("standard" / "vehicle" / "cframe" / "mobile"),未飞时为 nil |
参数与边界行为
| 项 | 说明 |
|---|---|
options.qefly | 布尔值,控制 Q/E 键是否作为上/下移动。默认 true。 |
setspeed 未开启飞行时调用 | 只更新内部速度变量,不影响当前状态。下次启动飞行时生效。 |
off 后查 isFlying | 一定返回 false。 |
getMode 未飞行时 | callback 收到 nil。 |
| 速度范围 | 推荐 0.5–10。过大可能触发反作弊。 |
APIFold["功能"]["飞行"]:Fire("standard", { speed = 2 })
APIFold["功能"]["飞行"]:Fire("isFlying", {
callback = function(isFlying)
-- isFlying = true / false
print("飞行中:", isFlying)
end,
})
APIFold["功能"]["飞行"]:Fire("getMode", {
callback = function(mode)
-- mode = "standard" / "vehicle" / "cframe" / "mobile" / nil
print("当前模式:", mode)
end,
})
APIFold["功能"]["飞行"]:Fire("off")
开始 / 切换 / 退出观战模式。本 API 无 callback。目标死亡、重生、离场或本地玩家重生时自动处理相机跟随与恢复。
APIFold["功能"]["观战"]:Fire("Event", somePlayer)
APIFold["功能"]["观战"]:Fire("Left")
{ callback = ... },直接接返回值:
-- ✅ 正确(同步返回)
local badge = APIFold["功能"]["定位"]["新的定位"]:Fire(somePlayer)
badge.StudsOffsetWorldSpace = Vector3.new(0, 10, 0)
-- ❌ 错误(callback 不会触发)
APIFold["功能"]["定位"]["新的定位"]:Fire(somePlayer, {
callback = function(badge) ... end -- 永远不会执行
})
local badge = APIFold["功能"]["定位"]["新的定位"]:Fire(somePlayer)
badge.StudsOffsetWorldSpace = Vector3.new(0, 10, 0)
APIFold["功能"]["定位"]["删除定位"]:Fire(somePlayer)
为自定义窗口绑定右下角缩放手柄。创建模板窗口 内部已自动调用。
APIFold["功能"]["窗口缩放"]["设置缩放"]:Fire(
myWindow, myFrame,
Vector2.new(300, 200),
Vector2.new(600, 500)
)
选择窗口 API
打开玩家选择窗口,自动列出当前服务器玩家,并标记「自己 / 好友 / 其他」三种角色。
| 参数 | 类型 | 说明 |
|---|---|---|
| X / Y | number | 鼠标位置(入场动画起点) |
| Goal | string | 窗口标题文案 |
| More | boolean | true 多选 / false 单选 |
| Myself | boolean | 是否把自己列在列表中 |
| Data.Callback | function | 接收 Player[];取消时收到 nil |
Callback 返回格式
| 情况 | callback 收到 |
|---|---|
| 用户点击了"选择"按钮 | Player[](选中玩家实例数组,至少 1 个) |
| 用户点击关闭 / 点击外部 | nil |
local UIS = game:GetService("UserInputService")
APIFold["创建"]["窗口"]["玩家选择窗口"]:Fire(
UIS:GetMouseLocation().X,
UIS:GetMouseLocation().Y,
"选择玩家",
false, -- More: 单选
true, -- Myself: 包含自己
{ callback = function(list)
if list then
for _, p in ipairs(list) do
print("选中:", p.Name, p.UserId)
end
else
print("用户取消")
end
end }
)
打开实例选择窗口。自动跳过所有含 Humanoid 的 Model(防止误选角色),大场景分帧遍历(每 50 节点 task.wait(0.1))。
| 参数 | 类型 | 说明 |
|---|---|---|
| X / Y | number | 鼠标位置 |
| Goal | Instance | 遍历起点,如 workspace |
| More | boolean | 是否多选 |
| Whitelist | string[]? | 只列出这些 ClassName;nil 表示不过滤 |
| Blacklist | string[]? | 排除这些 ClassName;与 Whitelist 二选一,Whitelist 优先 |
| All | boolean | true 递归遍历后代 / false 只看直接子级 |
| Data.Callback | function | 接收 Instance[];取消时收到 nil |
Callback 返回格式
| 情况 | callback 收到 |
|---|---|
| 用户点击"选择" | Instance[](选中实例数组) |
| 取消 | nil |
APIFold["创建"]["窗口"]["物品选择窗口"]:Fire(
UIS:GetMouseLocation().X,
UIS:GetMouseLocation().Y,
workspace, -- Goal
true, -- More: 多选
{ "Part", "MeshPart", "Model" }, -- Whitelist
nil, -- Blacklist
true, -- All: 递归
{ callback = function(list)
if list then
for _, obj in ipairs(list) do
print("选中:", obj:GetFullName())
end
end
end }
)
数据类 API
统一约定:Fire({ callback = function(value) ... end })。值通过 callback 返回。
| Key | callback 收到 | 说明 |
|---|---|---|
| 游戏名字 | string | Game Name |
| 游戏icon | string | "rbxassetid://xxx" |
| 游戏ID | number | PlaceId |
| 游戏开发者 | string | Creator.Name |
| 游戏描述 | string | Description |
| 服务器ID | string | JobId |
| 游戏缩略图 | string | 缩略图 URL |
APIFold["数据"]["游戏信息"]["请求"]["游戏名字"]:Fire({
callback = function(name)
-- name = "Grow a Garden" 等字符串
print("游戏名:", name)
end,
})
| Key | callback 收到 | 说明 |
|---|---|---|
| 日期(中国) | string | "星期一,六月29" |
| 日期 | string | "Monday, June 29" |
| 招呼(中国) | string | 早上好 / 下午好 / 晚上好 / 深夜好 |
| 招呼 | string | Good morning / afternoon / evening / late night |
| 时间(H) | string | 小时(如 "14") |
| 时间(M) | string | 分钟(两位) |
| 时间(S) | string | 秒(两位) |
| 时间(Y) | string | 年 |
| 时间(Mon) | string | 月 |
| 时间(D) | string | 日 |
| 时间(W) | string | 星期("1"–"7",1 = 周一) |
APIFold["数据"]["时间信息"]["请求"]["招呼(中国)"]:Fire({
callback = function(hi)
-- hi = "早上好" / "下午好" / "晚上好" / "深夜好"
print(hi, Player.DisplayName)
end,
})
官方多语言方法
MyTranslationsLau / BindText / BindTextAndName 不是框架原生 API,而是辅助代码片段。开发者需要自己复制到脚本里。
框架原生只暴露两样东西:
configurationFiles["语言"](StringValue)+ 它的.Changed事件;- 通知 API 的
content里,Text可以传函数(语言切换时自动重算)。
① 定义翻译表
function MyTranslationsLau()
local T = {}
if configurationFiles["语言"].Value == "zh-cn" then
T = { Title = "标题", Confirm = "确认" }
elseif configurationFiles["语言"].Value == "en" then
T = { Title = "Title", Confirm = "Confirm" }
elseif configurationFiles["语言"].Value == "zh-tw" then
T = { Title = "標題", Confirm = "確認" }
elseif configurationFiles["语言"].Value == "jp" then
T = { Title = "タイトル", Confirm = "確認" }
elseif configurationFiles["语言"].Value == "es-es" then
T = { Title = "Título", Confirm = "Confirmar" }
elseif configurationFiles["语言"].Value == "pt-br" then
T = { Title = "Título", Confirm = "Confirmar" }
else
T = { Title = "Title", Confirm = "Confirm" }
end
return T
end
② 绑定文本(TextLabel)
local function BindText(textLabel, key)
local function apply()
if textLabel and textLabel.Parent then
textLabel.Text = MyTranslationsLau()[key]
end
end
apply()
local conn = configurationFiles["语言"].Changed:Connect(apply)
textLabel.Destroying:Once(function()
if conn and conn.Connected then conn:Disconnect() end
end)
return conn
end
-- 用法
BindText(myLabel, "Title")
手动覆盖 Text 后的行为
如果某个 TextLabel 已被 BindText 绑定,手动改一次 .Text 后,下次语言切换时会被框架覆盖回去。应对方法:
- 临时覆盖:先保存
BindText返回的 connection,手动改前conn:Disconnect()。 - 完全替代:不用
BindText,自己监听configurationFiles["语言"].Changed。 - 分离节点:让框架绑一个隐藏的文本节点,显示用独立
TextLabel。
③ 绑定文本 + 对象名(按钮 / 列表项)
local function BindTextAndName(textLabel, object, key)
local function apply()
local text = MyTranslationsLau()[key]
if textLabel and textLabel.Parent then textLabel.Text = text end
if object and object.Parent then object.Name = text end
end
apply()
local conn = configurationFiles["语言"].Changed:Connect(apply)
if textLabel then
textLabel.Destroying:Once(function()
if conn and conn.Connected then conn:Disconnect() end
end)
end
return conn
end
-- 用法
BindTextAndName(button.Label, button, "Confirm")
④ 函数式文本(通知最灵活)
在通知的 content 规格表中,Text 可以直接传函数,语言切换时自动重新求值。仅通知支持,创建模板窗口、内容板块等场景需要自己实现。
APIFold["功能"]["通知"]:Fire({
mods = "Notice",
icon = { "rbxassetid://..." },
content = {
{ ClassName = "TextLabel",
Text = function() return MyTranslationsLau().Title end,
TextSize = 18,
BackgroundTransparency = 1,
AutomaticSize = Enum.AutomaticSize.XY,
TextColor3 = Color3.new(1, 1, 1) },
},
})
错误处理与异常行为
框架不做参数校验,绝大多数错误都被静默吞掉。以下是实测行为,开发者需要自己加防御。
| 异常场景 | 实际行为 | 建议 |
|---|---|---|
| 传错参数类型(如窗口 ID 传 number) | 静默失败或创建出异常 UI,不抛错 | 调用前用 typeof() 校验 |
| 父级在 callback 触发前被销毁 | callback 不执行 | callback 内部先判 if not target or not target.Parent then return end |
DMRIScriptCenter 被销毁后 Fire | 所有 APIFold.*:Fire() 报 attempt to index nil | 脚本开头 if not FoldAll then error(...) 兜底 |
| 路径写错 | attempt to index nil with 'Fire' | 用本文档的 getAPI(path) 辅助函数 |
执行器不支持 request | 玩家档案的粉丝/关注数显示 [?],其他字段正常 | 可忽略 |
执行器不支持 CreateHumanoidModelFromUserIdAsync | 3D 角色区域显示加载失败,窗口本身正常 | 提示用户 |
执行器不支持 setclipboard | 复制按钮点击后 warn 输出 | 可忽略 |
无 PlayerModule.ControlModule | 移动端飞行不可用(静默失效) | PC 飞行不受影响 |
实时列表未设置 AutomaticCanvasSize | 指示器静默不显示,不抛错 | 调用前检查并设置 |
callback 何时不触发
- 父级容器在异步构建过程中被
:Destroy(); - 用户主动关闭窗口(注意:玩家选择窗口取消时 callback 收到
nil,会触发); - 脚本被中断(执行器重载、
DMRIScriptCenter被清空)。
防御式模板
local ok, err = pcall(function()
assert(typeof(myButton) == "Instance" and myButton:IsA("GuiButton"),
"myButton 必须是 GuiButton")
APIFold["功能"]["菜单"]["弹出菜单"]:Fire(
{ Button = myButton, Mod = "Button2Click" },
{ Header = "操作", Options = { { Name = "查看", Icon = "rbxassetid://0" } } },
function(choice)
if not choice then return end -- 用户取消
-- ... 业务逻辑
end
)
end)
if not ok then
warn("[DMUI4] 菜单绑定失败:", err)
end
通用注意事项
时序问题
| 场景 | 推荐处理方式 |
|---|---|
| 创建Main左Tab 后立刻取 screen | 用 WaitForTab 封装(推荐)或 task.wait(2) |
| 创建内容板块 后立刻用 pinned | callback 返回,无需再等 |
| 创建模板窗口 后立刻改尺寸 | callback 里 task.wait(0.5) 或监听 ZIndex |
| 语言切换 后立刻读 UI 文本 | 等一帧 |
框架侧资源生命周期
| 操作 | 框架是否自动清理 |
|---|---|
win:Destroy()(模板窗口) | 是。内部连接全部断开,任务栏最小化条目自动移除。 |
pinned:Destroy()(内容板块) | 是。内部连接自动断开。 |
通知自然关闭 / DelKeep | 是。内部 NoticeID 表自动清除。 |
| 窗口最小化到任务栏 | 框架接管,不销毁实例。 |
开发者自己 Connect 的连接 | 否。需要开发者自己保存并在 Destroying 断开。 |
开发者对 configurationFiles 的监听 | 否。脚本销毁前必须手动 Disconnect。 |
连接管理示例
local conns = {}
local function track(c) table.insert(conns, c) return c end
track(btn.MouseEnter:Connect(function() ... end))
track(btn.MouseLeave:Connect(function() ... end))
win.Destroying:Once(function()
for _, c in ipairs(conns) do
if c and c.Connected then c:Disconnect() end
end
end)
执行器兼容性
| 功能 | 依赖 | 不支持时降级行为 |
|---|---|---|
| 基础 UI | 无 | — |
| 玩家档案 · 关注/粉丝数 | request / syn.request / http.request | 对应字段显示 [?] |
| 玩家好友状态 | 同上(走 RoProxy) | 好友图标隐藏 |
| 玩家档案 · 3D 角色 | CreateHumanoidModelFromUserIdAsync | 3D 区域显示加载失败 |
| 查看虚拟形象 | 同上 | 显示加载失败提示 |
| 复制 JobId / PlaceId | setclipboard | warn 输出,按钮无反应 |
| 移动端飞行 | PlayerModule.ControlModule | PC 飞行正常,移动端飞行不可用 |