设置 Schema 让扩展声明项目级别的配置项。创作者在 Studio 的扩展设置面板中就能调整这些值,不需要改代码。
基本用法
在 Extension 子类上声明 static settings,传给 settings() 一个 builder 函数:
import { Extension, extension, settings } from "@avg-studio/sdk";
@extension({ id: "save-screen", label: "存档画面" })
export class SaveScreen extends Extension {
static settings = settings((s) => ({
slotCount: s.number("存档槽位数").default(30).range(1, 200),
allowDelete: s.boolean("允许删除存档").default(true),
}));
}
Studio 会根据定义自动生成设置面板的 UI 控件。
字段类型
| 类型 | 构造方法 | 控件 | 链式 API |
|---|
| 字符串 | s.string(label) | 文本输入框 | .default() .multiline() .describe() |
| 数字 | s.number(label) | 数字输入框 | .default() .range(min, max) .step(n) .describe() |
| 布尔 | s.boolean(label) | 开关 | .default() .describe() |
| 枚举 | s.enum(label, values) | 下拉选择 | .default() .labels({...}) .describe() |
| 快捷键 | s.shortcut(label) | 快捷键录入 | .default() .describe() |
| 素材引用 | s.asset(label) | 素材选择器 | .accepts("image", "audio", "video", "any") .describe() |
| UI 引用 | s.uiRef(label) | UI 选择器 | .default() .describe() |
| 角色 | s.character(label) | 角色选择器 | .default() .describe() |
| 颜色 | s.color(label) | 取色器 | .default() .allowAlpha() .describe() |
| 列表 | s.array(label, fn) | 可增删的行列表 | .itemDefault({...}) .maxItems(n) .addLabel() .emptyHint() .describe() |
所有类型都支持 .enabledWhen(key, equals?) 做字段联动。
角色字段存的是角色 id 而不是名字 —— 创作者在角色模块改了显示名,配置不会失效。
列表字段
需要「数量不定的成组配置」时用 s.array(),比如按角色配颜色、按变量配提示文案。
固定数量的配置直接平铺写字段即可,不必用它。
static settings = settings((s) => ({
palette: s
.array("角色配色", (item) => ({
character: item.character("角色"),
mode: item
.enum("取色方式", ["theme", "custom"] as const)
.labels({ theme: "跟随角色预设色", custom: "自定义" })
.default("theme"),
color: item
.color("自定义颜色")
.default("#ffffff")
.enabledWhen("mode", "custom"),
}))
.itemDefault({ character: "", mode: "theme", color: "#ffffff" })
.maxItems(50)
.addLabel("添加角色")
.emptyHint("还没有配过任何角色。"),
}));
读出来是一个数组,每项是「子字段名 → 值」的对象:
const rows = ctx.settings.get<PaletteRow[]>("palette");
// [{ character: "char_yuki", mode: "theme", color: "#ffffff" }, ...]
几个要点:
- 行内子字段不支持嵌套列表。可用的类型是字符串、数字、布尔、枚举、素材、角色、颜色。
- 行内也能用
.enabledWhen(),依赖的是同一行里的另一个子字段(上面例子里「自定义颜色」只在取色方式为「自定义」时可编辑)。各行独立判定,互不影响。
.itemDefault() 是覆盖不是替换:没提到的子字段仍然落各自的 .default()。
链式 API 示例
static settings = settings((s) => ({
// 字符串,支持多行
welcomeText: s.string("欢迎语").default("欢迎来到游戏").multiline(),
// 数字,限制范围和步进
textSpeed: s.number("文字速度").default(50).range(10, 200).step(10),
// 枚举——第二个参数是值列表,用 as const 保留类型
theme: s.enum("主题风格", ["default", "retro", "modern"] as const)
.labels({ default: "默认", retro: "复古", modern: "现代" })
.default("default"),
// 快捷键
quickSaveKey: s.shortcut("快存快捷键").default("F5"),
// 素材引用
titleBgm: s.asset("标题 BGM").accepts("audio"),
titleBackground: s.asset("标题背景").accepts("image"),
// 总开关 + 子开关联动
showToolbar: s.boolean("显示工具栏").default(true),
showSaveButton: s.boolean("显示存档按钮").default(true).enabledWhen("showToolbar"),
}));
enabledWhen 联动
.enabledWhen(key, equals?) 让字段在同级的另一个字段值满足条件时才可编辑,否则置灰。equals 不传时默认为 true。
这是纯 UI 层的联动——置灰时不会改变字段的值,只是不让创作者编辑。
读取设置值
在扩展代码中通过 ctx.settings 读取:
function MyComponent() {
const ctx = useExtensionContext();
// React Hook(值变化时组件自动更新,返回 [value, setter] 元组)
const [subtitle, setSubtitle] = ctx.settings.useValue("subtitleText");
// 命令式读取
const slotCount = ctx.settings.get("slotCount");
return <div>{subtitle}</div>;
}
在非 React 上下文(如 onRegister)中可以用命令式订阅:
static onRegister(ctx) {
ctx.settings.subscribe("quickSaveKey", (newKey) => {
// 快捷键值变了,重新绑定
});
}
跨模块读取
一个扩展有多个 UI 模块时,用 ctx.settings.cross 读取其他模块的设置:
const showSkip = ctx.settings.cross.get("toolbar", "showSkipButton");
设置的存储
设置值存在项目的 project.json(extensionSettings[extensionId][key])中。当 UI 模块有自己的设置时,key 会自动加上 <uiId>. 前缀,避免多个模块之间的命名冲突。
设置 Schema 适合声明创作者在 Studio 中配置的项目参数(标题画面的背景图、槽位数量)。玩家在游戏中调整的运行时配置(文字速度、音量)应该用 ctx.config API。