下载 App

存档系统设计指南

精华修改于03/16615 浏览开发交流
多槽位云端存档系统设计
适用于 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 制造云端存档设计
别再烧积分了!从每次 2000 到每次 50 的血泪经验截图
别再烧积分了!从每次 2000 到每次 50 的血泪经验
适用场景:所有在用 TapTap Code 开发游戏的小白 难度:★☆☆☆☆ 背景最近群里总有人"炫耀"一次操作花了几千积分,说实话这不是值得高兴的事——这是在告诉所有人你的开发方式有根本性问题。我也走过这条弯路,所以来说说怎么改。 积分消耗的本质是什么积分 = AI 处理的 token 数量 = 你发给 AI 的内容 + AI 回给你的内容所以烧积分的本质只有两种: 你让 AI 做了大量无效/重
精华
82 赞
23 回复
如何提升你游戏中的UI美术水平截图
如何提升你游戏中的UI美术水平
一、请为你的游戏挑选一款合适的字体 很多开发者没有注意到其实UI界面中最重要的元素是字体,一款合适的字体能给整个游戏的界面感官带来蜕变。 请看案例 那么如何挑选适合自己的字体呢? 我总结出了一些小经验可供参考: 黑体:通用 宋体:古风、武侠、修仙 楷体:古风、武侠、修仙 圆体:卡通、动漫、Q版 科技:科幻、未来、电子 顺手分享一个免费可商用字体网站: 当然 如果你不知道自己的游戏适合什么风格
精华
126 赞
44 回复
01:29
官方阅,申请积分计划2,希望官方继续支持我完成这款游戏,截图
官方阅,申请积分计划2,希望官方继续支持我完成这款游戏,
视频内容只是一部分, 本游戏耗时3个月,目前完成进度百分之80, 是生存游戏,进入修仙界,走剧情找到宗门加入, 这时游戏才是真正开始, 提升实力境界后就可以变强,获取功法法术法宝, @视频内是游戏里挑战妖族入侵一种主流玩法,游戏就是围绕着这个来进行, 还有很多地方可以战斗,获得资源修炼变强,比如去藏书阁学习功法, 去法宝楼抽法宝,每一个宗门数十个法宝抽取 去修仙界探索,获得各自的资源,可能遇见妖兽
1 赞
3 回复
永恒虚空持续开发日记1——关于游戏中的新名词:场频截图
永恒虚空持续开发日记1——关于游戏中的新名词:场频
写在前边:首先感谢TapTap制造版主大大@汪崽 对于作品潜力的肯定,给与了我积分计划二的权限[表情_不好意思]。这边也在加紧开发改进各种功能中,期望尽快给大家带来成熟的版本。[表情_猫咪举手] 大家好,我是永恒虚空的制作人黑猫不详,大家叫我黑猫就好。从今天开始我将以后置开发日记的形式给大家分享我在TapTap制造中制作的游戏永恒虚空的开发过程,新人希望大家多多关照! 今天先给大家介绍我作品中最重
投票
3 赞
2D 等轴测视角:完整理论截图
2D 等轴测视角:完整理论
2D 等轴测视角:完整理论 一、视角的本质 什么是等轴测 人站在高处斜着往下看地面,地面上的方格子不再是正方形,而变成了菱形。这就是等轴测的视觉本质。 俯视(正上方看下去): 等轴测(斜上方看下去): ┌──┬──┬──┐ ◇ ├──┼──┼──┤ ◇ ◇ ├──┼──┼──┤ ◇ ◇ ◇ └──┴──┴──┘ ◇ ◇ ◇ 正方形网格经过旋转 + 压缩,变成了菱形网格。 为什么是 2:1
6 赞
浅记《阿方的旅程》开发历程截图
浅记《阿方的旅程》开发历程
预计阅读时长10分钟,欢迎您先品鉴游戏,再回来看这篇“流水账” 这是我第一次实际接触“用自然语言制作游戏”,也是我第一次接触游戏开发工作,所以一切都是从零开始,跌跌撞撞,懵懵懂懂。[表情_豹哭] 我是通过获得的本次开发资格,6月22日晚,在微信公众号收到了测试资格的消息,最开始公布主题“跳跃”的时候,脑子里最先想到的就是几款非常经典的游戏「马里奥」「Flappy Bird」「Doodle Jump
10 赞
1 回复
01:13
【游戏自荐】《王朝崛起》——一个人做的2D骑马砍杀,但真的上头截图
【游戏自荐】《王朝崛起》——一个人做的2D骑马砍杀,但真的上头
略沙盒,玩法硬核,还在修bug,但真的上头 先声明:我不是托,是开发者自己来安利的。如果你喜欢《骑马与砍杀》那种从零拉起部队、跑商、招降将领、攻城叛变的玩法,那这款游戏大概率会对你的胃口。 一、这游戏玩什么? · 超大的2D沙盒地图:64座城市 + 128个村庄,四大势力割据,满地图的蛮族部落。· 动态世界 & AI:AI势力之间会自己宣战、结盟、叛变;武将忠诚度低了会自立为王,最多能冒出8个新势
13 赞
7 回复
01:27
《大二暑假,我用AI做了一款属于自己的游戏》截图
《大二暑假,我用AI做了一款属于自己的游戏》
本人自制游戏现已加入taptap封闭测试(仅适配安卓机型)欢迎大家前来尝试(八月十号可以正式开始游玩) 如果有建议或者bug反馈可以加入qq群928501443 #发现好游戏 #游戏日常 #浅评一下 #今天游戏圈发生了啥
4 赞
1 回复
【UI教程】如何将已有的效果图应用为实际的UI界面截图
【UI教程】如何将已有的效果图应用为实际的UI界面
在经过了一些折腾和比较折磨的尝试后,我总算是摸清楚了嗒啦啦怎么把UI效果图转换为实际游戏中的UI组件,今天就给大家带来这部分的经验分享。 tips:1.本篇教程需要各位掌握最基本的ps使用,具体的操作部分我会大致讲解,但仍需各位自行学习ps的基本操作,部分可以由ai代劳的环节我会注明。 2.本篇教程需要在以我前两篇教程作为前置环节,请在阅读这两篇前置教程后再进行本篇教程内容的学习 在按照前
12 赞
5 回复
01:21
请都看过来~!!!截图
请都看过来~!!!
我是傻…… 很遗憾错的过了这次活动的报名,但刚好我的游戏也是在这个期间完成的,看了下大佬们的作品,感觉我做的还是太粗浅了。模型粗糙,UI也用的原始版本,很多地方都还需要打磨,可惜...这次赠送积分已经败光了。以后的积分大概只能先维护下游戏修复点bug,希望以后还能申请积分... 既然都这样了,我也想看看和大佬们的作品有多大的差距。 星三国/星三国纪元 (其实名字我都还没决定好) 游戏了一半灵感来自
2 赞
4 回复