template/docs/intermediate/flags.md
unanmed aea30987f6 docs(10-11): 修复加粗边界导致的不渲染
- flags.md 第 14/15 行加粗短语以标点结尾致 flanking 失败,调整结构使加粗内容以汉字结尾
- 全量扫描 docs/intermediate/*.md(排除代码 span/围栏),确认 0 处真实失效
- READER-REVIEW 追加 batch 11 记录
2026-10-19 19:43:40 +08:00

5.6 KiB
Raw Blame History

Flag 变量

有些状态要跨事件、跨楼层一直保留,还要跟着存档走——比如「Boss 是否已被击败」「这道门开过没有」「某处计数到了几」。这些状态放哪?Flag 系统(state.flags)就是数据端提供的全局可存档变量:任何需要跨事件、跨楼层保留的简单状态,都可以存进 Flag,它会随存档一起保存与恢复。

字段与字段值

Flag 里的每一项都对应一个字段(IFlagCommonField)。要读懂下面的接口清单,先分清两个概念:

  • 字段是一个对象,代表「这个变量本身」,提供 get()(取值)、set(value)(设置值)、add(value)(对数字值增减)等方法;
  • 字段值就是字段里存的那个普通值(数字、字符串、布尔等)。你平时读写的,大多是字段值。

系统接口因此分成两组,看名字就能分辨:

  • 返回或操作字段对象的:setField / getField / getOrInsert / getOrInsertComputed——它们返回字段对象本身;
  • 直接操作字段值的:setFieldValue / addFieldValue / getFieldValue / getFieldValueDefaults——只关心值,不暴露字段对象。

怎么选?只读写一次值时用「值」那组更简单,不必多一步 get();要多次操作同一个字段、或要复用字段对象(例如先拿到字段、再反复增减)时,用「字段对象」那组更省事。两者可以互相转换:

// 用「字段值」的写法:一次设置、一次增减
state.flags.setFieldValue('open-count', 0);
state.flags.addFieldValue('open-count', 1);

// 用「字段对象」的写法:拿到字段后反复操作
const field = state.flags.getOrInsert('open-count', 0);
field.add(1);
const value = field.get();

其实「值」那组就是对「字段对象」那组的简写:setFieldValue(name, v) 等价于 getField(name)?.set(v),getFieldValue(name) 等价于 getField(name)?.get(),addFieldValue(name, v) 等价于 getField(name)?.add(v),getFieldValueDefaults(name, d) 等价于 getOrInsert(name, d).get()。

全部接口

IFlagSystem 提供如下接口:

接口 说明
occupied(field) 判断字段是否被占用
setField(field, value) 以指定值插入字段,返回字段对象
getField(field) 取字段对象,不存在返回 null
getOrInsert(field, defaultValue) 取字段,不存在则以默认值创建
getOrInsertComputed(field, defaultValue) 同上,但默认值由函数惰性计算
deleteField(field) 删除字段
setFieldValue(field, value) 设置字段的值
addFieldValue(field, value) 对数字字段增减
getFieldValue(field) 取字段的值,不存在返回 undefined
getFieldValueDefaults(field, defaultValue) 取字段的值,不存在则以默认值创建并返回

getOrInsertComputed 只在字段确实不存在时才执行默认值函数,适合默认值计算量较大的场景。getFieldValue 没有默认值,需要默认值时用 getFieldValueDefaults。

state.flags.setFieldValue('door-open', true);
const open = state.flags.getFieldValue('door-open');

state.flags.addFieldValue('kill-count', 1);
const count = state.flags.getFieldValueDefaults('kill-count', 0);

使用建议

Flag 会进入存档,因此不建议滥用:不再需要的变量应及时用 deleteField 删除,否则它会一直留在存档里。能用局部状态或对象自身数据表达的,就不必放进 Flag。

if (!state.flags.occupied('temp-flag')) {
    state.flags.setFieldValue('temp-flag', 1);
}

// 用完后删除,避免残留进存档
state.flags.deleteField('temp-flag');

Flag 常与游戏关键动作配合使用,例如在钩子中读写,见钩子。

示例:记录开关与计数

目标:记下「Boss 已被击败」这个开关,以及「门被开过几次」这个计数——两者都要跟着存档走。

改哪里:你要记录这些状态的地方——例如某个事件函数或钩子里。

复制什么:

// 记录一个开关
state.flags.setFieldValue('boss-defeated', true);

// 记录一个计数
const times = state.flags.getOrInsert('open-count', 0);
times.add(1);

复制说明:片段已省略 import(这里用到的都是 state 上的成员);若编辑器提示缺少某个名字,再在顶部补对应的 import(详见 代码基础 的「照抄教程里的代码时,怎么处理 import」)。

改什么:把 'boss-defeated'、'open-count' 换成你的字段名,把 true、1 换成你的值。只需一次读写的开关用「值」那组更简单;要反复增减的计数,用 getOrInsert 拿到字段对象后再 add 更省事。

怎么验证:写入后读 state.flags.getFieldValue('boss-defeated') / getFieldValue('open-count'),应返回你写入的值;存档后再读档依然存在。

注意:Flag 会进存档,不再需要的变量要及时 deleteField,避免一直残留在存档里。

想从 state 出发找到 Flag 对象,见数据端总览。