DMUI 4 · API 参考

DMUI 4 运行在 Roblox 客户端执行器上。所有对外能力通过 DMRIScriptCenter.API 下的 BindableEvent 暴露。

诚实说明:DMUI 4 在 API 设计上并非完全统一,以下三类不一致是框架现状:
  • 大部分创建类走异步 callback,但 创建Main左Tab 没有 callback,需手动等待;
  • 几乎所有 Fire 都无返回值,唯独 定位.新的定位 同步返回 BillboardGui;
  • 框架不提供 UI 就绪事件,所有创建后等待依赖 task.wait 或轮询。

概览

完整 API 目录树

以下是从 DMRIScriptCenter.API 展开的完整 BindableEvent 树。键名区分大小写,写错任何一层都会 attempt to index nil。

API 完整层级
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)
防御写法:所有 Fire 前建议先 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
创建类·异步 callbackFire(参数..., { 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构建日期字符串
语言StringValuezh-cn / zh-tw / en / jp / es-es / pt-br
通知位置StringValue左 / 中 / 右
实时列表位置StringValue左 / 右
主颜色Color3ValueColor3.fromRGB(23,147,255)
主菜单状态IntValue1 双栏 / 2 只内容 / 3 只导航
通知设置/保存最大值IntValue5–50,默认 30
Spectate设置/菜单方向StringValue上 / 下

创建类 API

统一约定:Fire(参数..., { callback = function(result) ... end })。Fire 本身无返回值,结果一律通过 callback 异步返回。

Method 创建Main左Tab:Fire(BuildName: string, IconImage: string): ()

在主窗口创建一个带侧栏导航的 Tab。

参数类型说明
BuildNamestringTab 名称,同时作为 screen[BuildName] 与 primary[BuildName] 的 key
IconImagestring图标资源 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 秒。推荐使用轮询 + 超时封装:

WaitForTab / WaitForChild
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
Method 创建内容板块1:Fire(TaskName: string, Image: string, Parent: ScrollingFrame, LayoutOrder: number, Data: {Callback}): ()

在指定 ScrollingFrame 中创建一个带头部图标的内容板块。

参数类型说明
TaskNamestring板块标题
Imagestring板块图标资源 id
ParentScrollingFrame通常传上一步的 scroll
LayoutOrdernumber在同父级中的排序
Data.Callbackfunction接收 pinned 实例
关于 lable:框架内部标题节点命名确实是 lable(拼写错误),不是文档笔误。访问标题文本时必须写 lable,写 label 会得到 nil。

Callback 返回格式 · pinned 结构

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 }
)
Method 创建模板窗口:Fire(Title, MinX, MinY, MaxX, MaxY, BuildX, BuildY, CanMini, IconId, Scale, Data: {Callback}): ()

创建一个 Leaflet 风格的模板窗口。窗口自带拖拽、右下角缩放手柄、最小化到任务栏、关闭按钮、层级焦点管理。

参数类型说明
Titlestring窗口标题
MinX / MinYnumber最小内容尺寸(框架会加 150px 总边距)
MaxX / MaxYnumber最大内容尺寸(同样加 150px)
BuildX / BuildYnumber入场动画起始偏移,通常传鼠标位置
CanMiniboolean是否显示最小化按钮
IconIdstring最小化后任务栏图标
Scalenumber图标缩放
Data.Callbackfunction接收 WinUI 实例

尺寸与边界行为

问题实际行为
MinX / MaxX 是最终尺寸吗?不是。它们描述「内容区尺寸」,框架在四周各加 75px 边距。最终窗口宽度 = MinX + 150。示例传 300,最小总宽是 450。
BuildX/BuildY 传 0,0合法。入场从屏幕左上角播到中央。
BuildX/BuildY 传 nil等价 0,0,不报错。
CanMini=falsewin.frame.LeafletControls.Top.Small 节点不存在(不是隐藏)。访问得到 nil。
MinX/MinY 与 MaxX/MaxY 相等视为固定尺寸,右下角缩放手柄自动隐藏。

Callback 返回格式 · WinUI 完整层级树

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)     ← 缩放手柄(已自动绑定)

常用属性路径速查

路径类型可写属性 / 说明
winImageLabelSize / Position / ZIndex / ImageTransparency
win.frameImageButtonBackgroundColor3 / Size / ClipsDescendants / Active
win.frame.LeafletControls.Top.Title.ContainerTextButtonText(标题文本)/ TextSize / TextColor3 / FontFace
win.frame.LeafletControls.Top.Close.ButtonImageButton关闭按钮,已内置关闭动画
win.frame.LeafletControls.Top.Small.ButtonImageButton最小化按钮(仅 CanMini=true)
win.frame.LeafletControls.Bottom.ResizeImageButton缩放手柄(已自动绑定)
win.frame.SpaceFrame焦点层,点击置顶
窗口有 0.5s 入场动画。BuildX/Y 只决定动画起点,最终位置固定在屏幕中央。开发者内容应添加到 win.frame 下(不要加到 WinUI 本身,避免被 UIPadding 影响)。

防御写法(CanMini=false)

安全访问 Small
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 }
)
Method 实时列表:Fire(Scroll: ScrollingFrame): ()

给指定 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 返回。

Event 通知:Fire(Data: NoticeData): ()

创建通知。支持 Notice(临时)、Keep(常驻,需续期),以及基于 noticeId 的查询 / 续期 / 关闭。

字段类型说明
modsstringNotice / Keep / GetKeepTime / GetKeepTimeLeft / DelKeep
iconstring[]?图标资源 id 列表
contenttable | function?UI 规格表或构建函数
input{Text, TextColor}[]?仅 Notice,按钮列表
timenumber?仅 Notice,clamp 到 [5, 30]
callbackfunction?用户选择结果 / 超时为 nil
noticeIdnumber?查询 / 续期 / 关闭目标 ID
onCreatefunction?创建成功后收到分配的数字 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合法,用户选择结果被丢弃。
Notice 带按钮
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,
})
Keep 常驻 + 续期
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,
})
Keep 续期机制:每条 Keep 在 300 秒内未被续期会自动关闭。ID 生命周期:ID 只增不减,已关闭的 ID 不会被复用。
Event 菜单:Fire(Payload: {Button, Mod} | nil, Data: MenuData, Callback: function?): ()

弹出右键菜单或把菜单绑定到按钮。

字段类型说明
ButtonGuiButton | nil绑定目标;nil 时在鼠标位置直接弹出
ModstringButton1Click / Button2Click
Data.Headerstring?菜单顶部标题
Data.Optionstable{ Name, Icon, Primary? } 数组,或 "SEPARATOR"
Callbackfunction?接收选中项 Name;取消为 nil

Callback 返回格式

情况callback 收到
用户点击了某个选项string(该选项的 Name)
用户点击菜单外部 / 取消nil

Payload 为 nil 时的用法

第一个参数传 nil 时,菜单不绑定任何按钮,在鼠标当前位置直接弹出。适合快捷键弹菜单、右键空白处弹菜单场景。

不绑定按钮
APIFold["功能"]["菜单"]["弹出菜单"]:Fire(
    nil,   -- 不绑定按钮
    {
        Header = "快捷操作",
        Options = {
            { Name = "刷新", Icon = "rbxassetid://..." },
            { Name = "退出", Icon = "rbxassetid://...", Primary = true },
        },
    },
    function(choice)
        print("选中:", choice)
    end
)

分隔线配置

"SEPARATOR" 是固定常量字符串,不支持自定义高度或颜色。

绑定到按钮
APIFold["功能"]["菜单"]["弹出菜单"]:Fire(
    { Button = myButton, Mod = "Button2Click" },
    {
        Header = "玩家操作",
        Options = {
            { Name = "查看档案", Icon = "rbxassetid://126593296119593" },
            "SEPARATOR",
            { Name = "踢出", Icon = "rbxassetid://79900935590724", Primary = true },
        },
    },
    function(choice)
        print("选中:", choice)
    end
)
Button 必须是 GuiButton(ImageButton / TextButton)。触屏设备上 Button2Click 自动降级为长按 3 秒。
Event 飞行:Fire(Action: string, Options: FlyOptions?): ()

统一飞行控制。Action 取值见下表。

Action说明
standard标准飞行(键盘 WASD/QE + 移动端摇杆)
vehicle载具飞行(更高速度倍率)
cframeCFrame 飞行
mobile移动端摇杆飞行
toggle切换当前模式开/关
setspeed只改速度不切换
off关闭所有飞行
isFlying查询,callback 返回 boolean
getMode查询,callback 返回当前模式 string

Callback 返回格式

Actioncallback 收到
isFlyingboolean(true = 飞行中,false = 未飞)
getModestring("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")
Event 观战:Fire(Action: "Event" | "Left", Player?: Player): ()

开始 / 切换 / 退出观战模式。本 API 无 callback。目标死亡、重生、离场或本地玩家重生时自动处理相机跟随与恢复。

示例
APIFold["功能"]["观战"]:Fire("Event", somePlayer)
APIFold["功能"]["观战"]:Fire("Left")
Event 定位.新的定位:Fire(Object: Instance): BillboardGui
⭐ 这是框架中唯一的同步返回 API。不要写成 { 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)
Event 窗口缩放.设置缩放:Fire(Window: GuiObject, Frame: GuiObject, MinSize: Vector2, MaxSize: Vector2): ()

为自定义窗口绑定右下角缩放手柄。创建模板窗口 内部已自动调用。

示例
APIFold["功能"]["窗口缩放"]["设置缩放"]:Fire(
    myWindow, myFrame,
    Vector2.new(300, 200),
    Vector2.new(600, 500)
)

选择窗口 API

Method 玩家选择窗口:Fire(X: number, Y: number, Goal: string, More: boolean, Myself: boolean, Data: {Callback}): ()

打开玩家选择窗口,自动列出当前服务器玩家,并标记「自己 / 好友 / 其他」三种角色。

参数类型说明
X / Ynumber鼠标位置(入场动画起点)
Goalstring窗口标题文案
Morebooleantrue 多选 / false 单选
Myselfboolean是否把自己列在列表中
Data.Callbackfunction接收 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 }
)
Method 物品选择窗口:Fire(X: number, Y: number, Goal: Instance, More: boolean, Whitelist: string[]?, Blacklist: string[]?, All: boolean, Data: {Callback}): ()

打开实例选择窗口。自动跳过所有含 Humanoid 的 Model(防止误选角色),大场景分帧遍历(每 50 节点 task.wait(0.1))。

参数类型说明
X / Ynumber鼠标位置
GoalInstance遍历起点,如 workspace
Moreboolean是否多选
Whiteliststring[]?只列出这些 ClassName;nil 表示不过滤
Blackliststring[]?排除这些 ClassName;与 Whitelist 二选一,Whitelist 优先
Allbooleantrue 递归遍历后代 / false 只看直接子级
Data.Callbackfunction接收 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 返回。

Callback 数据.游戏信息.请求.<Key>:Fire({Callback}): ()
Keycallback 收到说明
游戏名字stringGame Name
游戏iconstring"rbxassetid://xxx"
游戏IDnumberPlaceId
游戏开发者stringCreator.Name
游戏描述stringDescription
服务器IDstringJobId
游戏缩略图string缩略图 URL
示例
APIFold["数据"]["游戏信息"]["请求"]["游戏名字"]:Fire({
    callback = function(name)
        -- name = "Grow a Garden" 等字符串
        print("游戏名:", name)
    end,
})
Callback 数据.时间信息.请求.<Key>:Fire({Callback}): ()
Keycallback 收到说明
日期(中国)string"星期一,六月29"
日期string"Monday, June 29"
招呼(中国)string早上好 / 下午好 / 晚上好 / 深夜好
招呼stringGood 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 可以传函数(语言切换时自动重算)。

① 定义翻译表

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

BindText
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 后,下次语言切换时会被框架覆盖回去。应对方法:

  1. 临时覆盖:先保存 BindText 返回的 connection,手动改前 conn:Disconnect()。
  2. 完全替代:不用 BindText,自己监听 configurationFiles["语言"].Changed。
  3. 分离节点:让框架绑一个隐藏的文本节点,显示用独立 TextLabel。

③ 绑定文本 + 对象名(按钮 / 列表项)

BindTextAndName
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 可以直接传函数,语言切换时自动重新求值。仅通知支持,创建模板窗口、内容板块等场景需要自己实现。

函数式 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玩家档案的粉丝/关注数显示 [?],其他字段正常可忽略
执行器不支持 CreateHumanoidModelFromUserIdAsync3D 角色区域显示加载失败,窗口本身正常提示用户
执行器不支持 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)
创建内容板块 后立刻用 pinnedcallback 返回,无需再等
创建模板窗口 后立刻改尺寸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 角色CreateHumanoidModelFromUserIdAsync3D 区域显示加载失败
查看虚拟形象同上显示加载失败提示
复制 JobId / PlaceIdsetclipboardwarn 输出,按钮无反应
移动端飞行PlayerModule.ControlModulePC 飞行正常,移动端飞行不可用