跳转到内容

存档与读档

SaveManager 把命名的存档槽位持久化为版本化的信封(envelope)。每份存档都记录它写入 时的 schema version,所以已发布的游戏之后可以修改存档结构、仍能读取旧存档——读档时 旧数据会被向前迁移。

为你的游戏创建一个管理器(选定当前的 schema version),然后按名字读写槽位:

import { SaveManager } from 'esengine';
const saves = new SaveManager({ version: 1 });
saves.save('slot1', { level: 3, score: 1200, hp: 80 });
const data = saves.load<{ level: number; score: number; hp: number }>('slot1');
// -> { level: 3, score: 1200, hp: 80 } (or null if the slot is empty)
saves.has('slot1'); // true
saves.savedAt('slot1'); // epoch ms the slot was written, or null
saves.remove('slot1'); // delete the slot
  • save(slot, data)data 打上当前版本与时间戳后写入。
  • load(slot) 返回槽位的数据(已向前迁移);槽位为空或无法读取时返回 null
  • has / remove / savedAt 分别用于查询、删除、取写入时间。

当存档结构变化时,提升 version 并为每一步加一个迁移函数。migrations[n] 把版本 n 写入的存档升级到 n + 1;读档时,从存档版本到当前版本的每一步按顺序依次执行:

const saves = new SaveManager({
version: 2,
migrations: {
// v1 stored a flat score; v2 nests it under `stats`.
1: (old) => {
const s = old as { level: number; score: number };
return { level: s.level, stats: { score: s.score, deaths: 0 } };
},
},
});
// A v1 save on disk is upgraded to the v2 shape transparently on load.
const data = saves.load('slot1');

迁移是单向向前的:读取比当前版本更新的存档会抛错(不支持降级),迁移链中缺步 同样抛错——这样缺口会被立即发现,而不是悄悄损坏数据。

对于不需要版本化的简单开关和偏好设置,直接用 Storage API——一个逐平台的键值存储 (浏览器 localStorage、微信存储……),带类型化的辅助方法:

import { Storage } from 'esengine';
Storage.setBoolean('muted', true);
Storage.setNumber('volume', 0.8);
Storage.setJSON('keybinds', { jump: 'Space', fire: 'KeyJ' });
Storage.getBoolean('muted', false); // true
Storage.getNumber('volume', 1); // 0.8
Storage.has('keybinds'); // true

Storage 还有 getString / setStringremove(key)clear()SaveManager 就构建在它之上,两者持久化到同一个逐平台后端。

没有目录可以打开,这是刻意的:Web 和小游戏平台根本没有路径命名空间可给,所以 getSaveDirectory() 会是一个五个平台里只有两个答得上来的函数,而每个调用它的游戏 都会因此长出逐平台分支。Storage 就是可移植的答案——写进去,各平台的耐久存储会被 替你选好。

平台 后端存储
Web localStorage
微信 / 小游戏 wx.setStorageSync
iOS Application Support/estella-storage.json
Android files/estella-storage.json(应用内部数据目录)
Node 仅内存——不跨进程存活

iOS 上用的是 Application Support 而不是 Caches,这是有意为之——iOS 想要回空间时随时 会清空 Caches,存在那里的存档玩家可能凭空丢失。两个移动平台的目录都是应用私有的,并且 会进入玩家换新手机时恢复的设备备份。

Estella 0.41 之前的构建把同一个文件放在缓存目录里。如果你已经发布过这样的版本,更新 后的第一次启动会找到那份存档并把它搬过来——玩家的进度不会丢,你也不需要写任何代码。

platformReadFile() 不是读它们的途径。它读的是打包进构建产物里的文件 (APK 的 assets/、iOS 的 app bundle),且只读。

  • 场景 —— 加载与切换存档所指向的场景。
  • 资源 —— 为什么大块数据应放资源而非存档槽。