设置 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.jsonextensionSettings[extensionId][key])中。当 UI 模块有自己的设置时,key 会自动加上 <uiId>. 前缀,避免多个模块之间的命名冲突。
设置 Schema 适合声明创作者在 Studio 中配置的项目参数(标题画面的背景图、槽位数量)。玩家在游戏中调整的运行时配置(文字速度、音量)应该用 ctx.config API。