用途
Godot 4.3+ 存档/读档系统实现辅助技能,帮助开发者针对不同数据类型选择正确的序列化策略。所有示例均面向 Godot 4.3+,不使用已废弃 API。
相关技能:resource-pattern(自定义 Resource 数据容器)、inventory-system(背包序列化模式)、godot-project-setup(SaveManager autoload 设置)、popochiu(冒险框架存档)。
策略对比
| 策略 | 最适合 | 可读性 | 编辑器支持 | 备注 |
|---|---|---|---|---|
| ConfigFile | 设置、简单键值数据 | 是 | 否 | 内置 INI 风格,无额外依赖 |
| JSON | 游戏存档、灵活结构 | 是 | 否 | 跨平台,可做版本迁移 |
| Resource .tres | 编辑器集成的数据 | 是 | 是 | 不安全——绝不加载不可信文件 |
| Resource .res | 快速二进制数据 | 否 | 是 | 不安全——绝不加载不可信文件 |
推荐策略时应指出被排除的方案及其决定因素,例如“选 JSON 而非 Resource 文件,因为存档由用户可写,而加载 Resource 会执行代码”。未指明替代方案的选择无法被推翻。
安全警告:加载 .tres 或 .res 文件会执行资源中内嵌的任意 GDScript。绝不从不可信来源(用户上传文件、下载的模组)加载 Resource 文件。用户生成的存档数据应使用 ConfigFile 或 JSON。
ConfigFile —— 设置
ConfigFile 写入 INI 风格的分节,适合音频/视频/控制设置(体积小、便于设计师调试)。使用 set_value(section, key, value) 后 save(path);用 load(path) 和 get_value(section, key, default) 读取。完整 GDScript + C# 设置存读及典型设置菜单接线见 references/configfile.md。
JSON —— 游戏存档
用 JSON.stringify(dict) 序列化,JSON.parse_string(text) 反序列化,通过 FileAccess 读写。适合需要人类可读文件的游戏存档。构建一个捕获全部游戏状态的 Dictionary(玩家位置、背包、世界标记),序列化后写入 user://save_<slot>.json。完整 GDScript + C# 实现(含 FileAccess 封装与错误处理)见 references/json-saves.md。
Godot 4.7 变更:JSON.stringify(data, indent = "", sort_keys = true, full_precision = false) 现在即使传入 indent,也会将空 Dictionary 紧凑序列化为 {}(GH-115883)。升级后,带 indent 写出的存档中空字典字段格式会变化——任何逐字节 diff 或哈希存档输出的逻辑必须容忍新格式。解析不受影响。
存档架构模式
对较大的游戏,为每个持久化节点附加 SaveableComponent。每个组件声明 save_callable 与 load_callable。存档管理器按 ID 遍历组件,调用各自的 save 可调用对象,构建主 Dictionary。完整的 SaveableComponent + 存档管理器实现见 references/save-architecture.md。
存档文件位置
user:// 解析为项目文件夹之外、平台特定的可写目录。
| 平台 | 路径 |
|---|---|
| Windows | %APPDATA%\Godot\app_userdata<project-name>\ |
| macOS | ~/Library/Application Support/Godot/app_userdata/<project-name>/ |
| Linux | ~/.local/share/godot/app_userdata/<project-name>/ |
存档数据始终使用 user://,绝不使用 res://。导出构建中 res:// 路径为只读。
版本迁移
存档文件的生命周期长于写入它的 schema。始终在存档 Dictionary 顶部包含 "version": <int>;加载时按版本 switch,将旧存档增量向前迁移(v1 → v2 → v3 → 当前)。绝不破坏旧存档,始终迁移。完整迁移助手模式见 references/version-migration.md。
实现检查清单
- 设置用 ConfigFile,游戏存档用 JSON(不用 Resource)
- 每个存档文件包含 version 整数字段
- 存档路径使用 user://,绝不使用 res://
- 写入存档前调用 DirAccess.make_dir_recursive_absolute()
- Vector2/Vector3 序列化为独立的 x/y/z 浮点数(JSON 无 Vector 类型)
- 所有文件操作检查返回码,失败时调用 push_error()
- _migrate() 处理从 0 到当前的所有版本,增量应用
- 玩家可控的存档数据绝不使用 Resource 文件(.tres/.res)
- 提供 get_save_slots() 与 delete_save() 辅助函数用于 UI 槽位管理
- 可存档节点使用会话间不变动的稳定 ID