pixi-vn-saves

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Pixi'VN Saves: exporting, restoring, and persisting game state

Pixi'VN 存档:导出、恢复和持久化游戏状态

Official docs: pixi-vn.com/start/save.
官方文档:pixi-vn.com/start/save。

When to use this skill

何时使用此 skill

Use this skill whenever a task involves saving/loading the game: a save-file format, save slots, a quick-save shortcut, "continue where I left off" after a page refresh, or exporting/importing a save as a file. This skill does not cover the module-level details of what's inside a save (storage variables —
pixi-vn-storage
; canvas element state —
pixi-vn-canvas
; step/backlog history —
pixi-vn-history
) — only the save/load flow itself and how those pieces fit together.
每当任务涉及保存/加载游戏时,都应使用此 skill:存档文件格式、存档槽、快速保存快捷键、页面刷新后的“从此处继续”,或将存档导出/导入为文件。此 skill 不涵盖存档内部的模块级细节(存储变量——
pixi-vn-storage
;canvas 元素状态——
pixi-vn-canvas
;step/backlog 历史记录——
pixi-vn-history
)——仅涵盖保存/加载流程本身,以及这些部分如何协同工作。

The core trio

核心三件套

ts
import { Game } from "@drincs/pixi-vn";

Game.exportGameState(): GameState
Game.restoreGameState(data: GameState): Promise<void>
Game.jsonToGameState(json: string): GameState
  • Game.exportGameState()
    returns a plain
    GameState
    object — version info plus the current storage, canvas, sound, and history data — suitable for
    JSON.stringify
    .
  • Game.restoreGameState(data)
    restores everything from a previously exported
    GameState
    , using whatever navigate function was set via
    Game.onNavigate
    /
    Game.init
    's
    navigate
    option — it no longer takes a navigate function as a second argument.
  • Game.jsonToGameState(json)
    just parses a JSON string back into a
    GameState
    — a thin wrapper around
    JSON.parse
    with the right type, for symmetry with
    exportGameState
    .
ts
const json = JSON.stringify(Game.exportGameState());
// ... later, possibly after a page reload ...
await Game.restoreGameState(Game.jsonToGameState(json));
ts
import { Game } from "@drincs/pixi-vn";

Game.exportGameState(): GameState
Game.restoreGameState(data: GameState): Promise<void>
Game.jsonToGameState(json: string): GameState
  • Game.exportGameState()
    返回一个普通的
    GameState
    对象,其中包含版本信息以及当前的 storage、canvas、sound 和 history 数据,适合通过
    JSON.stringify
    进行序列化。
  • Game.restoreGameState(data)
    使用通过
    Game.onNavigate
    或
    Game.init
    的
    navigate
    选项设置的 navigate 函数,从之前导出的
    GameState
    中恢复所有内容——它不再接收 navigate 函数作为第二个参数。
  • Game.jsonToGameState(json)
    只是将 JSON 字符串解析回
    GameState
    ——它是带正确类型的
    JSON.parse
    轻量封装,用于与
    exportGameState
    保持对称。
ts
const json = JSON.stringify(Game.exportGameState());
// ... later, possibly after a page reload ...
await Game.restoreGameState(Game.jsonToGameState(json));

What's inside a
GameState

GameState
包含哪些内容

A
GameState
bundles every module's current state: storage variables, the canvas's saved element/layer state (if the canvas was initialized), sound's channel/media state, and go-back history (capped by
stepHistory.stepLimitSaved
, default
20
— see
pixi-vn-history
for what that limit means for how far a player can go back after loading).
Game.exportGameState
/
restoreGameState
is the layer application code should use — there's no need to go through any individual module directly for a full game save.
GameState
打包了每个模块的当前状态:存储变量、canvas 已保存的元素/图层状态(如果 canvas 已初始化)、sound 的声道/媒体状态,以及后退历史(上限由
stepHistory.stepLimitSaved
控制,默认值为
20
——有关该上限对玩家加载后可后退多远的影响,请参阅
pixi-vn-history
)。
Game.exportGameState
/
restoreGameState
是应用代码在执行完整游戏存档时应当使用的层——无需针对完整游戏存档直接访问任何单独的模块。

Real-world persistence convention (official React template)

实际持久化约定(官方 React 模板)

Game.exportGameState()
says nothing about where that payload lives or what metadata a save-slot UI needs (title, timestamp, thumbnail). That part is entirely up to the app. The official React+TS template (
npm create pixi-vn@latest
, source at
src/models/GameSaveData.ts
/
src/lib/utils/save-utility.ts
) shows one concrete way to close that gap — this is the template's convention, not a library requirement:
ts
interface GameSaveData {
  saveData: GameState; // the raw Game.exportGameState() output
  gameVersion: string;
  date: Date;
  name: string;
  image?: string; // thumbnail, e.g. canvas.extractImage() — see pixi-vn-canvas
}

function createGameSave(options?: { image?: string; name?: string }): GameSaveData {
  return {
    saveData: Game.exportGameState(),
    gameVersion: __APP_VERSION__,
    date: new Date(),
    name: options?.name ?? "",
    image: options?.image,
  };
}

async function loadSave(saveData: GameSaveData) {
  await Game.restoreGameState(saveData.saveData);
}
The pattern: wrap
exportGameState()
's return value in an app-level object carrying save metadata, and pass the inner
saveData
field back into
restoreGameState()
when loading.
From there, the template persists that wrapped
GameSaveData
object (not the raw game state alone) through several concrete mechanisms — illustrative, not exhaustive:
  • Numbered save slots via IndexedDB (
    saveGameToIndexDB
    /
    getSaveFromIndexDB
    /
    deleteSaveFromIndexDB
    ), auto-incrementing from
    0
    .
  • Quick save slots, also via IndexedDB (
    quickSaveGameToIndexDB
    ), reserved to a fixed negative-id range below the numbered saves (e.g. ids
    -2..-7
    for 6 slots) so they never collide with manual saves. Each quick-save fills the first empty slot, then cycles by overwriting the least-recently-used slot once all slots are full.
  • An "auto exit save" (
    addAutoExitSave
    , paired with a
    useAutoSaveOnPageClose
    hook bound to the
    beforeunload
    and
    visibilitychange
    events) stringified straight into
    localStorage
    under its own key — not through IndexedDB — so an accidental tab close or reload doesn't lose progress.
  • A "Continue" button:
    getLastSaveFromIndexDB
    powers a main-menu "Continue" action by comparing the latest numbered IndexedDB save against the auto exit save and returning whichever is newer (synthesizing id
    -1
    for the auto exit save for that comparison) —
    null
    if neither exists, to disable the button; the result feeds straight into
    loadSave
    .
  • A downloadable/importable JSON file (
    downloadGameSave
    /
    loadGameSaveFromFile
    ) for backing up or sharing a save outside the app.
None of this is prescribed by
@drincs/pixi-vn
— treat it as one worked example of gluing
exportGameState
/
restoreGameState
to a real persistence layer, not the only way to do it.
Game.exportGameState()
并未规定该载荷应存放在何处,也未规定存档槽 UI 需要哪些元数据(标题、时间戳、缩略图)。这些完全由应用决定。官方 React+TS 模板(
npm create pixi-vn@latest
,源码位于
src/models/GameSaveData.ts
/
src/lib/utils/save-utility.ts
)展示了一种填补该空白的具体方式——这是模板采用的约定,并非库的强制要求:
ts
interface GameSaveData {
  saveData: GameState; // the raw Game.exportGameState() output
  gameVersion: string;
  date: Date;
  name: string;
  image?: string; // thumbnail, e.g. canvas.extractImage() — see pixi-vn-canvas
}

function createGameSave(options?: { image?: string; name?: string }): GameSaveData {
  return {
    saveData: Game.exportGameState(),
    gameVersion: __APP_VERSION__,
    date: new Date(),
    name: options?.name ?? "",
    image: options?.image,
  };
}

async function loadSave(saveData: GameSaveData) {
  await Game.restoreGameState(saveData.saveData);
}
该模式是:将
exportGameState()
的返回值包装在一个应用级对象中,并为该对象附加存档元数据;加载时,再将内部的
saveData
字段传回
restoreGameState()
。
接下来,模板通过几种具体机制持久化包装后的
GameSaveData
对象(而不是仅持久化原始游戏状态)——以下内容仅为示例,并不穷尽所有方式:
  • 通过 IndexedDB 实现编号存档槽(
    saveGameToIndexDB
    /
    getSaveFromIndexDB
    /
    deleteSaveFromIndexDB
    ),编号从
    0
    开始自动递增。
  • 快速存档槽同样通过 IndexedDB 实现(
    quickSaveGameToIndexDB
    ),使用编号存档下方的固定负数 ID 范围(例如用
    -2..-7
    表示 6 个存档槽),从而避免与手动存档发生冲突。每次快速保存都会填充第一个空槽;当所有槽位均已占用时,则循环覆盖最近最少使用的槽位。
  • “自动退出存档”(
    addAutoExitSave
    )与绑定到
    beforeunload
    和
    visibilitychange
    事件的
    useAutoSaveOnPageClose
    hook 配合使用,直接序列化到
    localStorage
    下的独立键中——而不经过 IndexedDB——这样意外关闭标签页或刷新页面也不会丢失进度。
  • “继续”按钮:
    getLastSaveFromIndexDB
    会将最新的编号 IndexedDB 存档与自动退出存档进行比较,并返回其中较新的一个(比较时为自动退出存档合成 ID
    -1
    );如果两者都不存在,则返回
    null
    ,以禁用该按钮;其结果可直接传递给
    loadSave
    。
  • 可下载/可导入的 JSON 文件(
    downloadGameSave
    /
    loadGameSaveFromFile
    ),用于在应用之外备份或分享存档。
以上方式均不是
@drincs/pixi-vn
的强制规定——应将其视为将
exportGameState
/
restoreGameState
连接到实际持久化层的一个完整示例,而不是唯一可行的实现方式。

Related skills

相关 skill

  • pixi-vn-getting-started —
    Game.init
    /
    Game.onNavigate
    and the rest of the top-level
    Game
    API.
  • pixi-vn-storage — variables/flags/stored classes that end up inside a save, and reserved keys.
  • pixi-vn-history —
    stepHistory.stepLimitSaved
    and what survives a save/load for "go back".
  • pixi-vn-canvas —
    canvas.extractImage()
    for save thumbnails, and what canvas state gets saved.
  • pixi-vn-migration — renames affecting this area (
    getSaveData
    →
    Game.exportGameState
    , etc.).
  • pixi-vn-getting-started——
    Game.init
    /
    Game.onNavigate
    以及其他顶层
    Game
    API。
  • pixi-vn-storage——最终会进入存档的变量/标志/存储类,以及保留键。
  • pixi-vn-history——
    stepHistory.stepLimitSaved
    ,以及在保存/加载后哪些内容仍可用于“后退”。
  • pixi-vn-canvas——用于存档缩略图的
    canvas.extractImage()
    ,以及哪些 canvas 状态会被保存。
  • pixi-vn-migration——影响此区域的重命名(
    getSaveData
    →
    Game.exportGameState
    等)。