今天第一篇讲「关卡怎么教玩家走路」(游戏设计)。这一篇换到技术侧,讲一件迟早要面对、且直接决定玩家会不会给你差评的事:存档。玩家最恨的不是游戏难,是「玩了 3 小时进度没了」。而存档做对了,还能白嫖 Steam 云存档——玩家换电脑也能接着玩,这是上架版独立游戏的标配体验。
今日速览
- 能写的目录只有一个:
user://。游戏运行时res://(项目目录)基本上是只读的;存档、设置这类持久数据必须写进user://,它在导出后也保证可写,且每个平台、每个用户各自独立。 - 官方主推的通用思路:给要保存的对象打上「Persist」组,让每个对象自己实现
save()返回一个字典,存档器统一遍历组、JSON.stringify()后逐行写盘;读档时反向instantiate还原。 - JSON 不是万能的:它不支持
Vector2 / Vector3 / Color / Rect2 / Quaternion,也没有自定义类型,需要你自己拆成 x/y 或转十六进制;文字格式还比二进制大得多。 - 数据量大或复杂就换二进制序列化(
var_to_bytes / bytes_to_var一族),JSON 只适合简单、想人工调试的状态。 - 上架加分项:Steam Auto-Cloud。在 Steamworks 后台配好路径,
user://里的存档会被 Steam 自动同步,零代码实现跨设备云存档;最佳实践是拆成「频繁变」和「不常变」几个小文件。
核心学习内容
一、先分清 res:// 和 user://,否则一定会写出「打包后存不上」
Godot 用 UNIX 风格正斜杠做路径,两个前缀含义完全不同:
res://= 项目资源目录(含project.godot的那个文件夹)。开发时能写,但导出后通常是只读的,不能拿它存玩家数据。user://= 用户数据目录,自动创建、保证可写、每用户独立。桌面端默认落在:- Windows:
%APPDATA%\Godot\app_userdata\[项目名] - macOS:
~/Library/Application Support/Godot/app_userdata/[项目名] - Linux:
~/.local/share/godot/app_userdata/[项目名]
- Windows:
如果你想让存档落到系统更「正规」的位置,可以在项目设置里打开 application/config/use_custom_user_dir,目录就变成 %APPDATA%\[项目名] 这种。结论:存档路径一律写 user://save_xxx.json,别用 res://。
二、通用存档流程:Persist 组 + JSON 逐行写
官方教程的做法很适合抄:
- 把需要进存档的节点加进名为
Persist的组(编辑器里选组,或add_to_group("Persist"))。 - 每个这样的节点实现
func save() -> Dictionary,把要保存的字段塞进字典返回。注意注释里那句:"pos_x": position.x—— Vector2 不能直接进 JSON,必须拆成标量。 - 存档器遍历
get_tree().get_nodes_in_group("Persist"),跳过没有save()或不是实例化场景(scene_file_path为空)的节点,JSON.stringify()后store_line()写盘。 - 读档时先清空旧的 Persist 节点(
queue_free()),再逐行get_line()→JSON.new().parse(),按filename加载场景instantiate()、挂到parent路径、还原位置和其余字段。
这样写的好处是可扩展:以后新增一种要存的物件,只要加进组、写好 save(),存档器一行都不用改。
三、JSON 的坑,以及什么时候该换二进制
JSON 简单、人可读、好调试,但官方明确列了限制:
- 文件体积大:文本格式比二进制大不少。
- 类型受限:
Vector2 / Vector3 / Color / Rect2 / Quaternion都不支持,要手动编码解码;自定义类也要自己写转换逻辑。 - 需要自己写编解码:好处是你能控制存档格式,坏处是字段一多就容易漏。
判断标准很简单:状态简单、想手改调试 → JSON;状态复杂/量大 → 二进制(bytes_to_var / var_to_bytes,官方有专门的 Binary serialization API 章节)。不要一开始就上二进制,很多小游戏 JSON 足够了。
另外两个实战提醒:① 官方示例假设 Persist 节点之间不嵌套,嵌套会得到非法路径,真要嵌套得分批先存父再存子;② 存档文件可能被玩家手动改坏,读档务必做容错——parse 失败要能跳过而不是崩溃。
四、白嫖 Steam 云存档:Auto-Cloud
Steam Cloud 会把指定文件自动同步到 Steam 服务器,玩家换电脑时启动游戏前自动下载。三种做法:
- Auto-Cloud:只在 Steamworks 后台填路径规则(如
%WinAppData%/...),不用改代码,最省事。 - Cloud API:代码里调
ISteamRemoteStorage读写,控制力更强。 - 最佳实践:写入路径要按 Steam 用户唯一(需要时用
GetSteamID/GetAccountID);尽量小、尽量拆——把「频繁变」和「不常变」的状态分成不同文件,避免每次退出都重传整包;单次写入上限 100MB,超过 256MB 会拖慢上传下载。
对独立游戏来说,先做 Auto-Cloud 就够,等真需要分区/跨平台再上 API。
🎯 DeepSeek 提示词
① 让 DeepSeek 生成一个可直接用的 Godot 4 存档管理器(多存档槽 + 版本号)
适用场景:你还没写存档,想要一个结构清晰、能直接接进项目的基类。
你是一位资深 Godot 4 游戏开发工程师。请用 GDScript 写一个通用存档管理器,要求:
【目标】一个 autoload 单例 SaveManager,支持多个存档槽,用 JSON 存到 user:// 下。
【必须实现】
1. save_game(slot:int) 与 load_game(slot:int) 两个主方法;
2. 遍历 "Persist" 组中每个节点,调用其 save() -> Dictionary,把结果汇总成一个
带 meta 信息的总字典 {"version": 1, "saved_at": <unix时间>, "nodes": [ ... ]};
3. 路径为 user://save_%d.json,用 FileAccess 写;读取时先判断
FileAccess.file_exists,不存在则安全返回 false;
4. 读取时对 JSON 解析失败做容错:打印错误信息,返回 false,绝不崩溃;
5. 提供 get_save_slots() 返回所有存在的存档槽编号。
【约束】
- 只用 Godot 4 API(JSON.stringify / JSON.new().parse / FileAccess);
- 不要用 res:// 存数据,只写 user://;
- 每个方法写简短中文注释,说明为什么这么做。
【输出格式】先给一句设计说明,再给完整 .gd 文件代码,最后列 3 条接入步骤。
② 让 DeepSeek 把某个游戏对象改造成「可存档」的
适用场景:你已经有一堆节点,想快速把它们接入 Persist 机制。
你是 Godot 4 工程师,帮我把下面这个节点改造成可被存档系统保存/还原的节点。
【输入】我会粘贴我的节点脚本。
【任务】
1. 定义 func save() -> Dictionary,返回所有需要跨存档保留的状态;
2. 定义 func load_data(data: Dictionary) -> void 把字典还原回节点;
3. 所有 Vector2 / Vector3 / Color 之类的 Godot 类型,必须拆成 JSON 能存的基本类型
(如 pos_x/pos_y),并在加载时能正确还原;
4. 在 _ready() 里加 add_to_group("Persist")。
【约束】
- 不要保存可从场景重新推导的临时状态(节点引用、运行时缓存等);
- 对字典里缺失的 key 要有默认值,保证旧存档也能读;
- 注释说明哪些字段为什么必须存。
【输出格式】在代码块里给出修改后的完整脚本,并在下方用表格列出「字段 → 类型 → 是否必存」。
③ 让 DeepSeek 当存档 bug 猎人(配合报错或表现使用)
适用场景:存档丢失、读档崩溃、升级后旧存档读不出等。
你是一位 Godot 4 存档系统 debug 专家。下面是我的存档相关代码与现象,
请系统性排查并给出修复方案。
【现象】<粘贴:例如「升级游戏后旧存档读不出」「退出再进进度归零」>
【代码】<粘贴存档器与相关节点脚本>
【排查清单,请逐条检查】
1. 是否误把数据写到 res://(导出后只读);
2. 是否有 JSON 不支持的类型(Vector2/Color 等)被直接塞进字典;
3. 是否存在 Persist 节点嵌套,导致 NodePath 失效;
4. 读档时是否忘记先清理旧节点,导致对象重复;
5. 是否缺少版本号与向后兼容的默认值;
6. parse 失败时是否有兜底,而不是直接崩溃。
【输出格式】
- 按上面的清单逐条给「是否有问题 + 证据 + 修复代码」;
- 最后给一个最小复现与验证步骤清单。
学习心得
存档是「默默无闻但决定口碑」的工程活。对「用 DeepSeek 做一款 Steam 游戏」这个目标来说,它尤其合适交给 AI:逻辑规整、套路固定、边界清晰,正好是 DeepSeek 生成代码的高命中区。我的打法是——先让 DeepSeek 按提示词①生成 SaveManager 骨架,再用提示词②逐个改造节点,最后把提示词③当存档系统的「单元测试」,每加一种新存档对象就让它审一遍。等本地存档稳了,Steamworks 后台配一次 Auto-Cloud,跨设备云存档几乎零成本到手——这是花最少力气、换玩家最高安全感的一步。
明日关注
- 把今天的 SaveManager 提示词真跑一遍,接进一个测试项目,验证「存档→退出→重进」闭环。
- 研究存档版本迁移的写法:
version字段 + 迁移函数表,让 v1 存档能被 v2 读取。 - 看 Steam Auto-Cloud 的路径规则写法,规划「设置 / 进度 / 元数据」该拆成哪几个文件。
本文由自动化学习流水线每日生成,坚持即学即记。
评论