pixi-vn-storage

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Pixi'VN Storage

Pixi'VN Storage

When to use this skill

何时使用此 skill

Use this skill whenever a task involves storing game state that must persist across steps and saves: flags ("has the player met Liam?"), counters (gold, affection points), arbitrary objects, or a custom class whose instances need to survive save/load (e.g. a quest-tracking model). It does not cover characters specifically (see the characters skill, which is itself built on top of the pattern described here) or how the history/go-back system is implemented internally (see the history skill).
Official docs (authoritative for terminology and recommended patterns): storage, flags, storage classes, temporary storage, save & load.
ts
import { storage, StoredClassModel } from "@drincs/pixi-vn";
(Also available from the narrower
@drincs/pixi-vn/storage
subpath — see
pixi-vn-getting-started
for when to prefer that.)
storage
is a ready-made singleton (
StorageManagerInterface
) — you never instantiate
StorageManager
yourself.
当任务涉及存储需要在各个步骤和存档之间持久化的游戏状态时,请使用此 skill:flags(“玩家是否已经遇见过 Liam?”)、计数器(金币、好感度值)、任意对象,或其实例需要在存档/读档后保留的自定义类(例如任务追踪模型)。本 skill 不专门涵盖角色相关内容(请参阅 characters skill;该 skill 本身构建于这里介绍的模式之上),也不涉及 history/go-back 系统的内部实现方式(请参阅 history skill)。
官方文档(术语和推荐模式的权威来源): storage, flags, storage classes, temporary storage, save & load.
ts
import { storage, StoredClassModel } from "@drincs/pixi-vn";
(也可从范围更窄的
@drincs/pixi-vn/storage
子路径导入——何时应优先使用该路径,请参阅
pixi-vn-getting-started
。)
storage
是一个现成的单例(
StorageManagerInterface
)——永远不要自行实例化
StorageManager
。

1. Simple variables:
storage.set
/
storage.get

1. 简单变量:
storage.set
/
storage.get

ts
storage.set("test", "test1");
storage.get("test"); // "test1"

storage.set("Test", "test2"); // keys are case-sensitive, "Test" !== "test"
storage.get("nope"); // undefined

storage.set("variable1", { test: "test", test2: 1 }); // objects/arrays are fine too
storage.set("variable6", null);
storage.set("variable7", undefined); // undefined removes the key entirely
Allowed value types (
StorageElementType
, from
src/storage/types/StorageElementType.ts
): strings, numbers, booleans,
null
/
undefined
, plain objects and arrays of these — no functions, class instances, or Maps/Sets. For anything richer, use a
StoredClassModel
(below).
Other
storage
methods:
  • storage.remove(key)
    — deletes a variable.
  • storage.flags.set(key, value: boolean)
    /
    storage.flags.get(key)
    — a dedicated boolean-flag store (docs), kept separate from
    set
    /
    get
    ; flags default to
    false
    when never set. Keys are case-sensitive here too. The docs recommend flags over
    storage.set
    for booleans specifically because of save size: internally all active flags are kept as a single array of names, which is much lighter to serialize than one storage entry per boolean. A documented pattern is exposing a class boolean property that's backed by a flag name (a string) instead of a literal
    boolean
    , so the getter/setter transparently reads/writes
    storage.flags.get
    /
    set
    .
  • storage.default = { key: value, ... }
    — sets the starting values used when the game boots or after
    storage.clear()
    ; if you later
    remove()
    a key (or it was never
    set()
    ), reading it falls back to this default instead of
    undefined
    .
  • storage.clear()
    — wipes all stored variables (used by tests/new-game flows), after which reads fall back to
    storage.default
    .
ts
storage.set("test", "test1");
storage.get("test"); // "test1"

storage.set("Test", "test2"); // keys are case-sensitive, "Test" !== "test"
storage.get("nope"); // undefined

storage.set("variable1", { test: "test", test2: 1 }); // objects/arrays are fine too
storage.set("variable6", null);
storage.set("variable7", undefined); // undefined removes the key entirely
允许的值类型(
StorageElementType
,来自
src/storage/types/StorageElementType.ts
):字符串、数字、布尔值、
null
/
undefined
、普通对象,以及由上述值组成的数组——不支持函数、 类实例或 Map/Set。对于更复杂的数据,请使用
StoredClassModel
(见下文)。
其他
storage
方法:
  • storage.remove(key)
    ——删除变量。
  • storage.flags.set(key, value: boolean)
    /
    storage.flags.get(key)
    ——专用的布尔标志存储(docs),与
    set
    /
    get
    分开管理;从未设置过的 flag 默认为
    false
    。这里的 key 同样区分大小写。文档建议在处理布尔值时优先使用 flags,而不是
    storage.set
    ,原因是保存体积更小:在内部,所有已启用的 flags 都会保存为一个名称数组,其序列化成本远低于为每个布尔值设置一个独立的 storage 条目。文档中记录的一种模式是公开一个由 flag 名称(字符串)作为底层支持的类布尔属性,而不是直接使用字面量
    boolean
    ,这样 getter/setter 就能透明地读写
    storage.flags.get
    /
    set
    。
  • storage.default = { key: value, ... }
    ——设置游戏启动时或
    storage.clear()
    之后使用的_初始_值;如果之后删除了某个 key(或从未
    set()
    过),读取它时会回退到此默认值,而不是返回
    undefined
    。
  • storage.clear()
    ——清除所有已存储变量(用于测试/新游戏流程),之后读取时会回退到
    storage.default
    。

Temporary (label-scoped) variables

临时(label 作用域)变量

See pixi-vn.com/start/temp-storage.
storage.temp.set(key, value)
behaves like
storage.set
for reads —
storage.get(key)
checks temp variables first, then falls back to the permanent store — but the value is deleted once the
label
it was set in closes. It's for scratch state that should reset once the player moves on:
ts
storage.temp.set("counter", counter + 1);
// ... reads still go through storage.get("counter")
storage.temp.remove("counter");
Per the docs' own description of the lifecycle: if the label that set the temp variable calls another label (nested call, e.g.
call
), the variable stays accessible from that child label too — it's only deleted once the originating label itself closes. But if control moves on via
jump
(closing the current label and starting a new one instead of nesting into it), the temp variable is gone immediately, since it belonged to the now-closed label. Implementation-wise this is tracked as a deadline against how many labels are open on the call stack at the moment
temp.set
runs — see
tests/storage.test.ts
,
setTempVariable & getTempVariable
, where a temp
counter
set inside a called label keeps incrementing across
narration.continue()
steps but is reset once
narration.labels.closeCurrent()
/
closeAll()
unwind the label stack. Don't rely on temp variables for state that must outlive the label that set it — use
storage.set
for that.
请参阅 pixi-vn.com/start/temp-storage。
storage.temp.set(key, value)
在读取方面表现得像
storage.set
——
storage.get(key)
会先检查临时变量,然后回退到永久存储——但该值会在设置它的
label
关闭时被删除。它适用于玩家继续推进后应重置的临时状态:
ts
storage.temp.set("counter", counter + 1);
// ... reads still go through storage.get("counter")
storage.temp.remove("counter");
按照文档本身对生命周期的描述:如果设置临时变量的 label 调用了另一个 label(嵌套调用,例如
call
),该变量在子 label 中也仍然可访问——只有当_最初设置它的_ label 本身关闭时才会被删除。但如果控制流通过
jump
继续前进(关闭当前 label 并启动一个新 label,而不是嵌套进入其中),临时变量会立即消失,因为它属于已经关闭的 label。从实现角度看,这是通过根据执行
temp.set
时调用栈中已打开的 label 数量计算一个截止层级来跟踪的——请参阅
tests/storage.test.ts
中的
setTempVariable & getTempVariable
,其中,在被调用 label 内设置的临时
counter
会在多个
narration.continue()
步骤中持续递增,但会在
narration.labels.closeCurrent()
/
closeAll()
展开 label 栈时重置。不要依赖临时变量保存必须比设置它的 label 存活更久的状态——请使用
storage.set
。

2. Custom persisted classes:
StoredClassModel

2. 自定义持久化类:
StoredClassModel

See pixi-vn.com/start/stored-classes. For a richer object whose instances need to persist (survive save/load) and whose fields should be individually get/set, extend
StoredClassModel
instead of storing a plain object with
storage.set
. Each instance is identified by a
categoryId
(shared by all instances of that class) plus a per-instance
id
; properties are read/written with the protected
getStorageProperty
/
setStorageProperty
helpers:
ts
import { StoredClassModel } from "@drincs/pixi-vn";

interface QuestProps {
  title: string;
}

class QuestModel extends StoredClassModel implements QuestProps {
  constructor(id: string, props: QuestProps) {
    super("quest", id); // categoryId, instance id
    this.defaultTitle = props.title;
  }
  readonly defaultTitle: string;

  get title(): string {
    return this.getStorageProperty<string>("title") ?? this.defaultTitle;
  }
  set title(value: string) {
    this.setStorageProperty("title", value);
  }

  get completed(): boolean {
    return this.getStorageProperty<boolean>("completed") ?? false;
  }
  set completed(value: boolean) {
    this.setStorageProperty("completed", value);
  }
}

const findLiam = new QuestModel("find_liam", { title: "Find Liam" });
findLiam.completed = true; // persists under the "quest" category
The constructor arg passed in (e.g.
props.title
) becomes the default, kept in memory on the instance;
setStorageProperty
writes an override into storage that takes priority when reading. Passing
undefined
to
setStorageProperty
removes that property from storage (falling back to the in-memory default on the next read). This is exactly the pattern
CharacterBaseModel
uses for
name
/
surname
/
age
— see the characters skill (the docs' own example uses a plain category name like
"city"
; the library's built-in models happen to fence theirs as
"___character___"
to avoid any accidental collision, but that underscore style is just a convention, not a requirement — a plain string like
"quest"
or
"city"
works the same way).
请参阅 pixi-vn.com/start/stored-classes。 对于需要持久化(能够在存档/读档后保留),且各字段应支持单独 get/set 的复杂对象, 请继承
StoredClassModel
,而不是使用
storage.set
保存普通对象。每个实例都由一个
categoryId
(该类的所有实例共享)和一个实例级
id
标识;属性通过受保护的
getStorageProperty
/
setStorageProperty
helper 读取和写入:
ts
import { StoredClassModel } from "@drincs/pixi-vn";

interface QuestProps {
  title: string;
}

class QuestModel extends StoredClassModel implements QuestProps {
  constructor(id: string, props: QuestProps) {
    super("quest", id); // categoryId, instance id
    this.defaultTitle = props.title;
  }
  readonly defaultTitle: string;

  get title(): string {
    return this.getStorageProperty<string>("title") ?? this.defaultTitle;
  }
  set title(value: string) {
    this.setStorageProperty("title", value);
  }

  get completed(): boolean {
    return this.getStorageProperty<boolean>("completed") ?? false;
  }
  set completed(value: boolean) {
    this.setStorageProperty("completed", value);
  }
}

const findLiam = new QuestModel("find_liam", { title: "Find Liam" });
findLiam.completed = true; // persists under the "quest" category
传入构造函数的参数(例如
props.title
)会成为_默认值_,保存在实例内存中;
setStorageProperty
会将覆盖值写入 storage,读取时优先使用该覆盖值。向
setStorageProperty
传入
undefined
会从 storage 中移除该属性(下次读取时回退到 内存中的默认值)。这正是
CharacterBaseModel
用于
name
/
surname
/
age
的模式—— 请参阅 characters skill(文档自身的示例使用类似
"city"
的普通 category 名称; 库内置的 model 恰好使用
"___character___"
这样的带围栏名称,以避免意外冲突, 但这种下划线风格只是一种约定,并非要求——类似
"quest"
或
"city"
的普通 字符串工作方式完全相同)。

3. Reserved keys

3. 保留键

Do not manually set/read storage keys that collide with the engine's own bookkeeping. The docs' rule of thumb (storage docs) is: system variables use a
___
-fenced prefix, so avoid that prefix in your own keys. The full reserved key list lives in the
SYSTEM_RESERVED_STORAGE_KEYS
constant (
src/constants.ts
) — in practice most entries are plain namespaced strings without the
___
fence (
"dialogue"
,
"dialogue:step_counter"
,
"choice:options"
,
"input:value"
,
"input:info"
,
"character"
,
"label:opened"
,
"choices:made"
, etc.), with
"___glue___"
being the one that actually uses the fenced style. Either way, treat every value in that constant as off-limits for your own
storage.set
/flag keys, to avoid confusion when inspecting exported save data.
不要手动设置或读取与引擎内部管理项冲突的 storage key。文档中的经验法则 (storage docs)是:系统变量使用 带
___
围栏的前缀,因此应避免在自己的 key 中使用此前缀。完整的保留 key 列表 位于
SYSTEM_RESERVED_STORAGE_KEYS
常量(
src/constants.ts
)中——实际上,大多数条目 都是不带
___
围栏的普通命名空间字符串(
"dialogue"
、
"dialogue:step_counter"
、
"choice:options"
、
"input:value"
、
"input:info"
、
"character"
、
"label:opened"
、
"choices:made"
等),其中
"___glue___"
才是真正使用带围栏 样式的条目。无论如何,都应将该常量中的每个值视为自己使用的
storage.set
/flag key 所不可用,以免在检查导出的存档数据时产生混淆。

4. Relation to save/load and history

4. 与存档/读档和历史记录的关系

Building save/load itself (the
Game.exportGameState()
/
restoreGameState()
flow, and how to persist the result) is covered by
pixi-vn-saves
— you generally don't touch storage directly for that; every variable set through
storage
is included automatically. The same is true for step-by-step rewind: a snapshot of storage is captured at each recorded history step, so
storage
variables participate in "go back" too, not just save/load — see
pixi-vn-history
.
storage.clear()
resets variables back to whatever was set via
storage.default
, which is what tests use to get a clean slate between scenarios.
构建 save/load 本身(
Game.exportGameState()
/
restoreGameState()
流程,以及如何 持久化结果)由
pixi-vn-saves
负责——通常不需要直接操作 storage;通过
storage
设置的每个变量都会自动包含在内。逐步 rewind 也是如此:每个已记录的 历史步骤都会捕获一份 storage 快照,因此
storage
变量不仅参与存档/读档,也参与 “go back”——请参阅
pixi-vn-history
。
storage.clear()
会将变量重置为通过
storage.default
设置的值,测试正是利用这一点在不同场景之间获得干净的状态。

Related skills

相关 skill

pixi-vn-getting-started, pixi-vn-saves, pixi-vn-characters, pixi-vn-history, pixi-vn-narration
pixi-vn-getting-started, pixi-vn-saves, pixi-vn-characters, pixi-vn-history, pixi-vn-narration