关于 Unity Save System
Unity Save System 是面向 Unity 6+ 开发者的保存系统实现指南,涵盖从简单偏好设置到本地与云端混合架构。内容强调原子写入、数据版本迁移、多存档槽和自动保存。
主要功能
- 01序列化游戏状态并持久化
- 02原子文件写入与备份恢复
- 03存档版本管理与迁移
- 04多槽位及自动保存
- 05UGS 云存档与可选加密
适用场景
- 01为 Unity 游戏实现进度存档
- 02处理存档格式变更与迁移
- 03在暂停或场景切换时自动保存
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ juliankerignard/ unity-skills --skill unity-save-system安装后在 Claude Code 中输入/unity-save-system确认已加载。
附带文件references/save-advanced.mdreferences/save-templates.md
详细介绍
Unity 保存系统
此技能的功能
指导在 Unity 6+ 中实现完整的保存系统:游戏状态序列化、通过原子写入持久化到文件、存档版本控制、多槽位管理、自动保存、云存档(UGS)和可选加密。涵盖从简单的 PlayerPrefs 到本地/云端混合架构。
先决条件
- Unity 6.0+(Awaitable API)
- 推荐:使用 Newtonsoft.Json(
com.unity.nuget.newtonsoft-json)进行高级序列化 - 简单场景无需严格依赖(JsonUtility 已足够)
快速入门
- 选择格式:JsonUtility(简单)或 Newtonsoft.Json(多态、Dictionary)
- 在每个需要保存的组件上创建
ISaveable接口 - 实现
SaveManager,在Application.persistentDataPath中执行原子写入 - 接入 自动保存(dirty flag + timer +
OnApplicationPause) - 测试 保存/加载流程,并编写 EditMode 测试(参见
/unity-test)
决策树
Que sauvegarder ?
|
+-- Preferences utilisateur (volume, langue, bindings) ?
| +-- Peu de donnees (<1 KB) --> PlayerPrefs
| +-- Plus structure ----------> JSON dans persistentDataPath
|
+-- Etat de jeu (progression, inventaire, position) ?
| +-- Slot unique -----------> JSON + ISaveable pattern
| +-- Multi-slots -----------> SaveManager avec index + slots nommes
| +-- Donnees volumineuses (>10 MB) --> Format binaire (MemoryPack/MessagePack)
|
+-- Cloud sync ?
| +-- Unity Gaming Services --> UGS Cloud Save
| +-- Cross-platform natif --> Platform Toolkit (Unity 6.3+)
| +-- Hybride (local + cloud) -> ISaveStorage abstraction (voir `references/save-advanced.md`)
|
+-- Sensible (anti-cheat) ?
+-- Encryption AES + hash d'integrite (voir `references/save-advanced.md`)
分步指南
步骤 1 — 选择序列化格式
| 标准 | JsonUtility | Newtonsoft.Json | MemoryPack | MessagePack |
|---|---|---|---|---|
| 设置 | Unity 内置 | Unity Package | NuGet | NuGet |
| 多态 | 否 | 是(TypeNameHandling) | 是 | 是 |
| Dictionary | 否 | 是 | 是 | 是 |
| 可读性 | JSON | JSON | 二进制 | 二进制 |
| 性能 | 快 | 中等 | 非常快 | 快 |
| 大小 | 中等 | 中等 | 小 | 小 |
| 推荐用途 | 原型开发 | 默认推荐 | >10 MB 数据 | 二进制替代方案 |
步骤 2 — 创建 ISaveable 和 SaveData
- 定义
ISaveable:SaveKey、CaptureState()、RestoreState(object) - 创建包含
version字段、Dictionary<string, object>和元数据的SaveData - 完整代码(ISaveable、SaveData、SaveManager)参见
references/save-templates.md
步骤 3 — 实现 SaveManager
- 使用单例或服务定位器模式(参见
/unity架构) - 注册
ISaveable(Register/Unregister) SaveAsync:捕获所有状态 -> 序列化 -> 原子写入(.tmp -> .save,保留 .bak)LoadAsync:读取文件 -> 反序列化 -> 恢复所有状态- 路径:
Path.Combine(Application.persistentDataPath, "saves", slotName + ".save")
步骤 4 — 自动保存
- Dirty flag:每个
ISaveable在状态变化时发出通知 - 可配置计时器(默认 60-120s)
- 在
OnApplicationPause(true)和场景切换时保存 - 自动保存部分参见
references/save-advanced.md
步骤 5 — 测试保存/加载流程
- EditMode 测试:保存 -> 清除状态 -> 加载 -> 断言状态已恢复
- 损坏测试:加载截断的文件 -> 验证是否回退到 .bak
- 迁移测试:使用 v2 代码加载 v1 存档
严格规则
始终
- 始终使用原子写入(写入 .tmp、重命名、保留 .bak)
- 始终对
SaveData进行版本控制(每次架构变更都递增version字段) - 始终使用
Application.persistentDataPath(绝不使用dataPath或streamingAssetsPath) - 始终在移动设备上通过
OnApplicationPause(true)保存 - 始终序列化 POCO(Plain Old C# Objects)——不要使用 Unity 类型
- 始终处理“没有存档”的情况(首次启动)
绝不
- 绝不使用
BinaryFormatter——已知存在安全漏洞,且已弃用 - 绝不在存档中存储
MonoBehaviour、GameObject或ScriptableObject引用 - 绝不在主线程上同步保存大于 100 KB 的文件
- 绝不在写入新文件前删除旧文件(崩溃时必然导致数据损坏)
- 绝不忽略 I/O 异常——始终使用 try/catch 并提供回退方案
相关技能
/unity-code-gen— 生成 ISaveable 和 SaveManager 样板代码/unity-test— 用于验证保存/加载周期的 EditMode 测试/unity-debug— 诊断反序列化问题/addressables— 通过地址字符串保存资源引用
故障排除
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
加载时出现 FileNotFoundException | 首次启动,没有存档 | 加载前检查 File.Exists(),并返回默认数据 |
| 更新后数据丢失 | Schema 变更但未迁移 | 添加 version 字段 + v1->v2->vN 迁移链 |
JsonSerializationException | 无法识别多态类型 | Newtonsoft:TypeNameHandling.Auto + 自定义 SerializationBinder |
| 存档损坏(JSON 被截断) | 写入期间崩溃 | 原子写入(.tmp -> rename)+ 回退到 .bak |
UnauthorizedAccessException | 路径未获授权(Android 作用域) | 仅使用 persistentDataPath |
| PlayerPrefs 消失(iOS) | 系统清理或重新安装 | 将重要数据迁移到文件中 |
| 存档过大(>50 MB) | 对大量数据进行 JSON 序列化 | 改用 MemoryPack/MessagePack,并压缩(GZip) |
| 云端/本地不同步 | 版本冲突 | 实现冲突解决机制(时间戳或版本计数器) |
评论
支持 Markdown