今日速览
- 存档的本质:把场景里散落的「状态」收集成「数据」,下次启动再反向还原——Godot 官方推荐用**分组(Group)**标记需要存档的节点
- 标准流程:每个节点实现
save()返回字典 →JSON.stringify序列化 →FileAccess写入user://路径;读档时逐行JSON.parse→load()场景重建节点 →set()还原属性 - JSON 存不了
Vector2 / Vector3 / Color / Quaternion,位置要拆成pos_x / pos_y分别存 - 数据量大或类型复杂时换二进制序列化(
store_var/get_var):文件更小、类型支持更全、自定义类不用手写编解码 - 用户设置(音量、画质)别混进存档,用专门的
ConfigFile类
核心学习内容
1. 存档第一步:圈出「要存的东西」
官方做法是把需要持久化的节点加入 “Persist” 分组(编辑器或代码里都行),存档时:
var save_nodes = get_tree().get_nodes_in_group("Persist")
for node in save_nodes:
if !node.has_method("save"): continue # 没有 save() 的节点跳过
var node_data = node.call("save")
save_file.store_line(JSON.stringify(node_data))
每个节点自己写 save() 返回字典,load() 时从字典还原。谁的数据谁负责,存档管理器只做「收集 + 写入」,游戏加新系统时不用改管理器——这就是官方强调的”可扩展存档”。
2. 序列化与写入:JSON + user:// 组合拳
- 路径用
user://savegame.save:Godot 自动映射到系统的用户数据目录(Windows 是%APPDATA%,Steam 云存档也认这个目录),打包发布后依然可写; - 字典里所有值必须是 JSON 支持的类型:
Vector2等要拆字段存(pos_x/pos_y),自定义类要自己写转换逻辑; - 每行一条 JSON 记录,天然支持多个存档对象,也方便调试(记事本直接打开看)。
3. 读档:反着来一遍
读档三步:① 检查文件存在 → ② 清场(queue_free() 掉现有 Persist 节点,避免克隆重复)→ ③ 逐行解析,load(filename).instantiate() 重建节点、add_child 到原 parent、set() 还原其余属性。
两个易踩的坑:Persist 节点不要互相嵌套(parent 路径会失效,嵌套就分阶段存、先存父);读档时游戏状态重置逻辑因项目而异,官方明确说”这部分要自己根据项目定制”。
4. 什么时候换二进制?
JSON 优势是人类可读、好调试;但文本体积大、类型有限。当存档包含复杂状态或自定义资源时,用 FileAccess 的 store_var(node_data) / get_var() 走二进制序列化:文件小、几乎支持所有类型、自定义类免手写编解码(注意只有带 PROPERTY_USAGE_STORAGE 标志的属性才会被序列化)。小游戏起步用 JSON 足够,别过度设计。
🎯 DeepSeek 提示词
提示词 1:完整存档管理器(autoload + 分组模式)
你是一位 Godot 4 游戏开发专家。请为我实现一个通用存档系统,包含两部分:
1. SaveManager.gd(autoload 单例):
- save_game(slot_index: int):遍历 "Persist" 分组的节点,调用每个节点的 save() 方法,把返回的字典用 JSON.stringify 序列化后写入 user://save_{slot_index}.save(一行一个对象,参考 Godot 官方 Saving games 教程)
- load_game(slot_index: int):文件不存在则返回 false;先释放场景中现有的 Persist 分组节点,再逐行读取、JSON.parse,用 node_data["filename"] 实例化场景、add_child 到 node_data["parent"]、恢复 pos_x/pos_y 并 set() 其余属性;解析失败打印错误并跳过该行
- 提供 has_save(slot_index) 和 delete_save(slot_index) 辅助方法
2. 附一份「节点接入示例」:一个 CharacterBody2D 玩家脚本如何实现 save() / load_from_dict(data) 两个方法接入系统
输出格式:完整可运行的 .gd 代码 + 每段关键代码一行中文注释 + 一个 100 字的使用说明。
约束:只用 Godot 4.x 内置 API;save() 返回的字典只能包含 JSON 兼容类型(Vector2 要拆成 pos_x/pos_y);处理"节点缺少 save() 方法"的情况。
适用场景:第一次给游戏加存档功能。生成后把玩家、背包、关卡等节点加入 Persist 分组并实现 save()/load 方法即可接入。
提示词 2:多存档槽 + 自动存档 + 存档信息预览
你是 Godot 4 游戏开发者。请扩展一个存档系统,增加以下能力:
1. 多存档槽(最多 3 个):每个槽独立文件 user://save_{slot}.save,互不影响
2. 自动存档:提供 autosave() 方法,调用时写入 user://autosave.save,并记录时间戳;游戏主循环每 60 秒自动触发一次
3. 存档预览信息:每次保存时额外写入一个 save_info_{slot}.json,包含 {slot, play_time_seconds, level_name, player_level, timestamp},供存档选择界面显示"第 3 关 · 5小时 · Lv.8"
4. 损坏容错:load 时若某一行 JSON 解析失败,跳过该行并继续加载其余数据,同时返回"存档有损坏行"的警告
要求:给出每个新增功能的函数签名和实现思路,关键函数给出完整代码;说明自动存档与手动存档在 UI 上如何区分(角标/文字)。
适用场景:存档从”能存能读”升级为”玩家可用”的完整功能——选存档、自动存档、坏档不崩溃,都是 Steam 玩家很在意的体验。
提示词 3:存档格式升级与迁移(防”更新毁档”)
你是 Godot 4 游戏开发专家。我的游戏存档是 JSON 格式(user://save_0.save,一行一个对象),现在要新增字段并重构数据结构,需要写一个迁移工具:
1. 在存档文件头部加入格式版本号(如第一行存 {"format_version": 2})
2. 写 migrate_save(old_path, new_path) 函数:读取旧文件,检测版本号;若版本低于当前版本,按迁移规则逐行转换(例如:旧字段 "hp" 改名为 "current_health" 并乘以 2,新增默认值字段 "mana": 100),写入新文件
3. 版本升级失败时保留旧文件备份(save_0.save.bak),不覆盖原档
4. 打印迁移日志:每行说明"哪条记录做了什么转换"
输出格式:完整迁移脚本代码 + 迁移规则表(旧字段 / 新字段 / 转换逻辑 / 默认值)+ 使用步骤。
约束:迁移必须幂等(重复执行结果一致);不允许静默丢弃数据,无法转换的字段要打印警告。
适用场景:游戏迭代加了新系统(技能树、背包)后,老玩家存档还能用。Steam 正式发布后”更新毁档”是差评高发区,迁移工具越早备越好。
学习心得
存档是”玩家时间”的保险柜——对独立游戏来说,存档坏了 = 玩家流失。好消息是存档系统是高度模式化的代码:分组标记、字典收集、序列化、重建节点,四步套路固定,正是 DeepSeek 最擅长生成的类型。今天学到的核心是”先定结构再写代码”:让 DeepSeek 先按官方 Persist 模式生成管理器,再加多槽位和自动存档,最后补迁移工具,三步就能得到一个发布级的存档系统,全程不用手写一行。
明日关注
- 用提示词 1 生成 SaveManager,接入玩家节点实测「存档→重启→读档」全流程
- 测试边界情况:存档文件被手动改坏一行,验证损坏容错是否生效
- 查一下 Steamworks 的云存档(Cloud Storage)接口,为后续接入 Steam 版做准备
本文由自动化学习流水线每日生成,坚持即学即记。
评论