Files
zeling_v2/Docs/DesignSpec/13_SaveSystem.md
2026-05-08 11:04:00 +08:00

6.1 KiB
Raw Permalink Blame History

13 · 存档系统规范

所属文档集 ← 返回索引
摘要存档数据结构Schema、读写契约、版本迁移与存档槽管理。


目录

  1. 存档设计原则
  2. 存档数据 Schema
  3. 存档读写契约
  4. 存档触发时机
  5. 版本迁移规范
  6. 存档槽管理

1. 存档设计原则

原则 说明
单一来源 存档数据是游戏状态的唯一持久化来源
原子性 存档要么完整写入,要么不写(不产生损坏的存档)
版本兼容 旧版存档可被新版游戏读取(向前兼容)
与引擎无关 存档格式为纯数据JSON / 结构化二进制),不依赖引擎序列化

2. 存档数据 Schema

2.1 顶层结构

DataModel SaveData {
    ──── 元信息 ────────────────────────
    schemaVersion   : Integer           // 用于版本迁移
    saveSlot        : Integer           // 存档槽编号1-3
    lastSaveTime    : String            // ISO 8601 时间戳
    playTimeSecs    : Integer           // 游戏内游玩时长(秒)
    
    ──── 玩家状态 ──────────────────────
    player          : PlayerSaveData
    
    ──── 世界状态 ──────────────────────
    world           : WorldSaveData
    
    ──── 进程 ──────────────────────────
    progression     : ProgressionSaveData
    
    ──── 经济 ──────────────────────────
    economy         : EconomySaveData
}

2.2 玩家存档数据

DataModel PlayerSaveData {
    currentHP       : Integer
    maxHP           : Integer
    maxSoulPower    : Integer
    maxSpiritPower  : Integer
    springCount     : Integer
    geoAmount       : Integer
    
    currentFormId   : ID
    unlockedFormIds : List<ID>
    
    equippedCharmIds: List<ID>          // 当前装备的护符
    inventoryItems  : List<{itemId: ID, count: Integer}>
    
    lastBonfireId   : ID                // 死亡后重生位置
    lastRoomId      : ID                // 关闭游戏前所在房间
    lastPosition    : Vector2           // 关闭游戏前的精确位置
}

2.3 世界存档数据

DataModel WorldSaveData {
    visitedRoomIds  : List<ID>
    activatedBonfireIds: List<ID>
    
    defeatedBossIds : List<ID>
    permanentlyDeadEnemyIds: List<ID>   // 不刷新的特殊敌人
    
    openedGateIds   : List<ID>
    activatedSwitchIds: List<ID>
    
    collectedCollectibleIds: List<ID>
    
    geoShade        : Optional<GeoShadeState>  // 见 09_ProgressionSystem
}

2.4 进程存档数据

DataModel ProgressionSaveData {
    unlockedAbilityIds: List<ID>
    
    mainQuestStage  : Integer
    completedQuestIds: List<ID>
    worldFlags      : Map<String, Boolean>  // 任意世界状态标记
    
    notchCount      : Integer
    purchasedNotchCount: Integer
    
    loreFoundIds    : List<ID>
    secretsFoundIds : List<ID>
    
    lootPityCounters: Map<ID, Integer>     // 保底计数itemId → 未掉落次数)
}

2.5 经济存档数据

DataModel EconomySaveData {
    ownedCharmIds   : List<ID>             // 已获得(非已装备)的护符
    shopPurchaseHistory: Map<ID, List<ID>> // shopId → 已购买 itemId 列表
}

3. 存档读写契约

Interface ISaveSystem {
    // 写入
    save(data: SaveData, slot: Integer) → SaveResult
    
    // 读取
    load(slot: Integer) → Optional<SaveData>
    
    // 检查
    slotExists(slot: Integer) → Boolean
    getSlotMetadata(slot: Integer) → Optional<SaveSlotMeta>
    
    // 删除(需二次确认)
    deleteSlot(slot: Integer) → Void
}

DataModel SaveSlotMeta {
    slot            : Integer
    lastSaveTime    : String
    playTimeSecs    : Integer
    lastRoomName    : String
    playerHP        : Integer
    maxHP           : Integer
}

DataModel SaveResult {
    success         : Boolean
    error           : Optional<String>
}

4. 存档触发时机

触发事件 存档类型 说明
OnBonfireRested 完整存档 最主要的存档时机
OnCollectiblePickedUp(关键道具) 完整存档 防止关键进程丢失
OnBossDefeated 完整存档 Boss 死亡立即存档
OnAbilityUnlocked 完整存档 防止能力解锁丢失
游戏退出(发出退出信号) 位置快照 保存位置,不算正式存档进度

设计决策:不提供手动存档按钮
原因:存档点机制是游戏节奏设计的一部分,强制存档会破坏张力


5. 版本迁移规范

5.1 版本号规则

  • schemaVersion 每次修改 SaveData 结构时递增
  • 不向后兼容(新版存档无法在旧版游戏中读取)
  • 向前兼容(旧版存档可被新版游戏迁移后读取)

5.2 迁移契约

Interface ISaveMigrator {
    canMigrate(fromVersion: Integer, toVersion: Integer) → Boolean
    migrate(data: RawSaveData, fromVersion: Integer) → SaveData
}

5.3 迁移策略

变更类型 处理方式
新增字段 旧存档中该字段赋默认值
删除字段 读取时忽略多余字段
字段重命名 迁移器执行映射
字段类型变更 迁移器执行转换逻辑
无法迁移 提示玩家,提供新开局选项(不自动删除)

6. 存档槽管理

规格项
存档槽数量 3 个
每槽独立 完全独立,互不影响
新游戏 选择存档槽 → 若已有存档显示警告 → 确认后覆盖
存档文件位置 用户数据目录(不与游戏安装目录混存)
备份 写入前备份旧文件为 .bak,写入成功后删除备份