下载 App

存档系统设计指南

精华修改于03/16600 浏览开发交流
多槽位云端存档系统设计
适用于 WASM/移动端场景的云端优先、本地缓存的多槽位存档方案。
1. 设计背景与约束
| 约束                  | 说明                                           |
| --------------------- | ---------------------------------------------- |
| WASM 无持久存储       | 每次启动本地存储为空,不能依赖本地文件做主存储 |
| 云变量单 key 大小限制 | 单个 key 的 value 不能超过约 10KB              |
| 存档数据会持续膨胀    | 随游戏进度增加,总数据量可能达到几十 KB        |
| 需要多存档槽位        | 玩家可管理多个独立存档                         |
2. 整体架构
┌────────────────────────────────────┐
│         业务层 (游戏逻辑)           │
│  Save() / SaveNow() / MarkDirty() │
└──────────────┬─────────────────────┘
               │
               ▼
┌────────────────────────────────────┐
│       SlotSaveSystem (唯一接口)     │
│  序列化 · 分片 · 云端读写 · 迁移    │
└───────┬──────────────┬─────────────┘
        │              │
        ▼              ▼
   ┌─────────┐  ┌────────────┐
   │本地文件  │  │ 云变量 API  │
   │(同步写)  │  │ (异步读写)  │
   └─────────┘  └────────────┘
核心原则:
云端是权威数据源,启动只从云端加载
本地是运行时缓存,先同步写本地保证不丢,再异步上传云端
SlotSaveSystem 是唯一入口,业务层不直接接触云变量或本地文件
3. 云端 Key 布局
cloud keys:
├── save_meta                # 全局概要 (所有槽位的摘要信息)
│
├── s_1_head                 # 槽位1 分片头 (索引 + 校验)
├── s_1_groupA               # 槽位1 数据组A
├── s_1_groupB               # 槽位1 数据组B
│
├── s_2_head                 # 槽位2 (同上结构)
├── s_2_groupA
│ ...
└── s_N_...                  # 槽位N
3.1 save_meta
存储所有槽位的摘要信息,用于存档选择界面快速展示:
lua
save_meta = {
    version    = 1,
    activeSlot = 2,          -- 上次使用的槽位
    slots = {
        ["1"] = {            -- 每个槽位的摘要
            label1    = ...,
            playTime  = 36000,
            timestamp = 1710590400,
            createdAt = 1710000000,
        },
        ["2"] = { ... },
    },
}
3.2 分片 head
每个槽位有一个 head key,记录该槽位所有数据组的索引和校验信息:
lua
s_N_head = {
    format    = 1,               -- 分片格式版本号
    version   = 6,               -- 存档数据版本号
    timestamp = 1710590400,
    slotId    = N,
    keys = {
        groupA = {
            cs  = 123456,        -- 校验码
            len = 2048,          -- 长度
        },
        groupB = {
            cs  = 789012,
            len = 3072,
        },
    },
}
4. 数据分组
将完整存档数据拆分为多个功能组,每组独立编码为 JSON 并存到单独的 key:
lua
local function SplitIntoGroups(saveData)
    return {
        core     = { ... },   -- 核心进度 (等级/关卡等)
        currency = { ... },   -- 货币类数据
        items    = { ... },   -- 物品/装备
        skills   = { ... },   -- 技能/天赋
        misc     = { ... },   -- 杂项配置
    }
end
5. 校验机制
每个分组在编码时计算 DJB2 哈希,写入 head。读取时重新计算并比对,确保数据完整性。
6. 生命周期流程
6.1 初始化 (Init)
从云端读取 save_meta
有 meta → 回调 (meta, isNewPlayer=false)
无 meta → 检查旧格式 key,执行迁移
网络错误 → 指数退避重试,全部失败则回调错误
6.2 加载存档 (LoadSlot)
读取 sNhead
从 head.keys 收集所有分组 key
批量读取所有分组
每组: 校验 DJB2 → JSON 解码
合并所有组 → 完整 saveData
版本迁移 → 反序列化到运行时状态
计算离线时长 → 设置 saveConfirmed = true
6.3 保存 (Save / SaveNow / MarkDirty)
Serialize() → 收集运行时状态为 saveData
SaveLocal(saveData) → 原子写入本地文件
SplitIntoGroups(saveData) → 拆分为功能组
每组: JSON 编码 → 计算校验码
异步云端批量写入 (BatchSet): head + 所有分组 key
更新 save_meta
6.4 新建存档 (CreateNewSlot)
重置运行时状态到初始值
序列化 → 本地保存 → 云端保存
更新 save_meta(添加新槽位摘要)
6.5 删除存档 (DeleteSlot)
校验: 不能删除当前活跃存档
从 save_meta 移除该槽位
云端批量删除该槽位的所有 key
7. 自动保存循环
在每帧 Update 中管理:
累计游戏时长
云端重试计时器(保存失败后的重试)
自动保存计时器:每 SAVE_INTERVAL 秒触发 Save()
脏标记计时器:MarkDirty() 后 DIRTY_DELAY 秒触发 Save()
8. 版本迁移
存档数据包含 version
字段。加载时通过迁移链逐级升级:
```lua
local MIGRATIONS = {
    [1] = function(data) ... end,   -- v1 → v2
    [2] = function(data) ... end,   -- v2 → v3
}
local function RunVersionMigrations(data)
    while data.version < CURRENT_VERSION do
        local migrateFn = MIGRATIONS[data.version]
        if migrateFn then
            migrateFn(data)
            data.version = data.version + 1
        else
            break
        end
    end
end
```
9. 容错策略
| 场景             | 处理                                      |
| ---------------- | ----------------------------------------- |
| 云端读取失败     | 指数退避重试,最多 N 次                   |
| 分片校验不匹配   | 回退到旧格式单 key 读取                   |
| 云端保存失败     | 本地已保存不阻塞游戏,后台定时重试        |
| 本地写入失败     | 仅打印警告,依赖云端                      |
10. 接口设计
```lua
-- 初始化 (启动时调用一次)
SlotSaveSystem.Init(onMetaReady)
-- 存档操作
SlotSaveSystem.LoadSlot(slotId, onComplete)
SlotSaveSystem.CreateNewSlot(slotId, onComplete)
SlotSaveSystem.DeleteSlot(slotId, onComplete)
SlotSaveSystem.CopyToSlot(targetSlot, onComplete)
SlotSaveSystem.SaveAndUnload(onComplete)
-- 保存触发
SlotSaveSystem.Save()        -- 常规保存
SlotSaveSystem.SaveNow()     -- 立即保存
SlotSaveSystem.MarkDirty()   -- 标记脏数据, 延迟合并
-- 每帧调用
SlotSaveSystem.Update(dt)
-- 查询
SlotSaveSystem.GetMeta()          -- 返回 save_meta
SlotSaveSystem.GetActiveSlot()    -- 当前槽位号
SlotSaveSystem.IsSaveHealthy()    -- 存档健康状态
```
11. 关键设计决策
| 决策                         | 理由                                           |
| ---------------------------- | ---------------------------------------------- |
| 云端优先,本地缓存           | WASM 每次启动本地为空,必须以云端为权威        |
| 先写本地,再异步上云         | 保证运行中不丢数据,云端失败不阻塞游戏         |
| 数据分组                     | 绕过单 key 大小限制,且各组独立互不影响        |
| DJB2 校验                    | 确保数据完整性                                 |
| 迁移链逐级升级               | 任意旧版本存档都能加载                         |
| 原子本地写入 (.tmp + rename) | 防止写入中断导致文件损坏                       |
| MarkDirty 合并机制           | 高频小改动不触发高频保存,延迟合并后一次性写入 |
12. 业务层需实现的扩展点
| 扩展点                        | 说明                               |
| ----------------------------- | ---------------------------------- |
| SplitIntoGroups(saveData)
| 定义分组策略,将完整数据拆为命名组 |
| MergeGroups(groups)
       | 将各组合并还原为完整数据           |
| Serialize()
               | 从运行时状态收集数据               |
| Deserialize(data)
         | 将数据写入运行时状态               |
| BuildMetaSlot()
           | 构建当前槽位的摘要信息             |
| MIGRATIONS[version]
       | 版本迁移函数                       |
总结
云端优先的设计,确保 WASM 环境下的数据持久化
数据分组解决单 key 大小限制
本地缓存保证数据安全
多槽位管理满足玩家需求
完善的容错机制确保系统稳定
通过 SlotSaveSystem 作为唯一接口,业务层可以专注于游戏逻辑,无需关心底层存储细节。
猜你想搜
taptap 制造云端存档设计
03:06
为了不被斩杀,我只能做到这些了截图
为了不被斩杀,我只能做到这些了
之前一直申请不到计划3,之前说太花哨,后面说体量完成度不足,在我一番小作文下,编辑终于给了我解答,什么原生质感,玩家开局体验啥的。 本来计划2也够用,准备以后再优化了,结果计划2赠送的积分要砍半了,学生党实在消费不起,于是只能再冲一波计划3了 从周六中午到现在一天半时间,优化了一遍角色卡,战斗卡,还有抽卡界面,优化了新手教程,该说不说确实比之前要舒服些。 前半部分为新版,后半部分为旧版。 明天再次
44 赞
9 回复
【新游上线】末日100天截图
【新游上线】末日100天
大家好,我们是《末日100天》的开发者Paul~ 这是一款围绕「生存-经营-轮回」展开的废土策略小游戏:白天分配 3 点行动力经营据点,夜里迎接十天一循环的尸潮,每张地图第 100 天还有一场首领战等着你。五张地图、五大区域机制、可继承的轮回遗产,以及钻石商城里的抽奖机——我们希望每次重开都有一点点新意。 以下是游戏简单介绍: 1.游戏背景 废土历元年,未知病毒席卷全球,城市化为死地。幸存者们退守
9 赞
6 回复
如何提升你游戏中的UI美术水平截图
如何提升你游戏中的UI美术水平
一、请为你的游戏挑选一款合适的字体 很多开发者没有注意到其实UI界面中最重要的元素是字体,一款合适的字体能给整个游戏的界面感官带来蜕变。 请看案例 那么如何挑选适合自己的字体呢? 我总结出了一些小经验可供参考: 黑体:通用 宋体:古风、武侠、修仙 楷体:古风、武侠、修仙 圆体:卡通、动漫、Q版 科技:科幻、未来、电子 顺手分享一个免费可商用字体网站: 当然 如果你不知道自己的游戏适合什么风格
精华
124 赞
44 回复
别再烧积分了!从每次 2000 到每次 50 的血泪经验截图
别再烧积分了!从每次 2000 到每次 50 的血泪经验
适用场景:所有在用 TapTap Code 开发游戏的小白 难度:★☆☆☆☆ 背景最近群里总有人"炫耀"一次操作花了几千积分,说实话这不是值得高兴的事——这是在告诉所有人你的开发方式有根本性问题。我也走过这条弯路,所以来说说怎么改。 积分消耗的本质是什么积分 = AI 处理的 token 数量 = 你发给 AI 的内容 + AI 回给你的内容所以烧积分的本质只有两种: 你让 AI 做了大量无效/重
精华
69 赞
21 回复
制造做《布衣江湖》踩过的5个大坑截图
制造做《布衣江湖》踩过的5个大坑
#开发心得 大家好,我是《布衣江湖》的开发者。 先坦白:我是个游戏小白,第一次做游戏,什么都不懂。今天借这个帖子,把这一路踩的坑写出来,希望能帮到同样在TapTap制造上摸索的兄弟们,也给自己留个记录。 **坑一**:玩家面板和怪物属性“各过各的” 刚开始做的时候,我花了好几天调玩家属性,把攻击、防御、气血都配得很漂亮。结果去打副本发现——怪物只有21点血,玩家一刀就能秒。 原因是:AI只改了玩
【作品自荐】我的小小冒险团截图
【作品自荐】我的小小冒险团
【作品自荐】我的小小冒险团 — 颇有深度但易上手的冒险经营RPG 大家好,我是霜蚀工作室的开发者,今天来自荐我的游戏《我的小小冒险团》。(怎么连自荐贴都一股AI味) 一句话定位: 这是一款融合据点经营、角色养成、回合制战斗、装备收集和PVP竞技(不强制)的冒险RPG。游戏融合了《腐烂国度2》的据点管理、常规RPG的角色培养、一点点据点防御战、以及我前作《冒险伊始2·军团》的世界观。 核心玩法: 🏠
2 赞
【UI教程】如何将已有的效果图应用为实际的UI界面截图
【UI教程】如何将已有的效果图应用为实际的UI界面
在经过了一些折腾和比较折磨的尝试后,我总算是摸清楚了嗒啦啦怎么把UI效果图转换为实际游戏中的UI组件,今天就给大家带来这部分的经验分享。 tips:1.本篇教程需要各位掌握最基本的ps使用,具体的操作部分我会大致讲解,但仍需各位自行学习ps的基本操作,部分可以由ai代劳的环节我会注明。 2.本篇教程需要在以我前两篇教程作为前置环节,请在阅读这两篇前置教程后再进行本篇教程内容的学习 在按照前
11 赞
5 回复
永恒虚空持续开发日记1——关于游戏中的新名词:场频截图
永恒虚空持续开发日记1——关于游戏中的新名词:场频
写在前边:首先感谢TapTap制造版主大大@汪崽 对于作品潜力的肯定,给与了我积分计划二的权限[表情_不好意思]。这边也在加紧开发改进各种功能中,期望尽快给大家带来成熟的版本。[表情_猫咪举手] 大家好,我是永恒虚空的制作人黑猫不详,大家叫我黑猫就好。从今天开始我将以后置开发日记的形式给大家分享我在TapTap制造中制作的游戏永恒虚空的开发过程,新人希望大家多多关照! 今天先给大家介绍我作品中最重
投票
3 赞
01:07
我给制造开发了一个半自动的开发工作流截图
我给制造开发了一个半自动的开发工作流
随着多模态模型识图能力增强,尝试一下AI 是否能自己修复bug,自己检查。 1,基于画面修复代码 2,点击TapTap制造 菜单,切换屏幕显示 3,截图验证 刚跑通一个小闭环,给大家分享一下 随着模型能力增强,Token费用走低,这种半自动开发是未来常态了
1 赞
2 回复
开发心得-界面布局心得截图
开发心得-界面布局心得
大家好,我正在用 TapTap 制造制作一款2D 治愈林间徒步旅行游戏《徒步荒野》。 我分享一下我的开发心得,希望能有用,下面是一些界面布局的心得。 第一步:定版式、操作 先确定游戏是横版还是竖版、需要哪些操作按钮,敲定整体玩法思路。 我的玩法灵感参考《边境之旅》,主打散步看风景、沿途触发随机事件。 原版是 3D,考虑到自己是新手,选择2D 横版街机式来做,制作更简单、好上手。 第二步:敲定全局美
6 赞
3 回复