今日速览
- Steam 玩家默认「关游戏不丢进度」是底线,存档系统是上架前必须做的基本功
- Godot 官方推荐套路:把要保存的节点加入 Persist 分组,每个节点自己写
save()/load()方法 - 存档格式首选 JSON(人类可读、易调试),但
Vector2/Color等类型要手动拆成数字再存 - 读档前必须先重置场景状态(删掉旧的 Persist 节点),否则会克隆出一堆重复对象
- 设置类小数据用
ConfigFile(INI 格式、分 section),敏感数据可用save_encrypted()加密
核心学习内容
1. 三件套:存什么、存哪、怎么存
存什么:用「分组」标记。把所有需要跨会话保留的节点加入 Persist 组,存档时一行代码拿到全部:
var save_nodes = get_tree().get_nodes_in_group("Persist")
存哪:Godot 提供 user:// 路径(Windows 上在 %APPDATA%,每台机器不同),存档必须写这里,绝不能写项目目录(发布后只读)。存怎么:FileAccess.open("user://savegame.save", FileAccess.WRITE) 打开文件,每个节点把数据存成一行 JSON。
2. 序列化:每个节点自己负责自己
给每个 Persist 节点写一个 save() 方法,返回一个字典:
func save():
return {
"filename": get_scene_file_path(), # 读档时用这个重新实例化场景
"parent": get_parent().get_path(), # 记录挂在哪个节点下
"pos_x": position.x, # Vector2 不被 JSON 支持,拆开存
"pos_y": position.y,
"current_health": current_health,
}
主存档器只做三件事:遍历分组 → 调 save() → JSON.stringify() 后 store_line()。每个节点存一行,读档时逐行解析、独立恢复,互不干扰。
3. 读档:先清场,再逐行还原
读档流程有顺序讲究:先检查文件是否存在 → 把场景里现有 Persist 节点全部 queue_free() 清掉(避免重复)→ 逐行读、JSON.parse() → 用 filename load().instantiate() 重建节点 → add_child 到记录的 parent → 遍历字典把属性 set() 回去。注意:如果 Persist 节点互相嵌套(A 是 B 的父节点),必须分两阶段加载:先建父、再挂子,否则路径会失效。
4. 进阶:ConfigFile 与二进制序列化
玩家设置(音量、画质、键位)别塞进存档,用 ConfigFile:set_value("Graphics","fullscreen",true) + save("user://settings.cfg"),天然分节、可手写注释。存档变大变复杂后,可用 FileAccess.get_var()/store_var() 做二进制序列化:文件更小、原生支持 Vector2/Color 等类型、自定义类无需手写编解码,代价是不可读、难调试。中小型游戏先 JSON 就够。
🎯 DeepSeek 提示词
提示词 1:生成完整的存档管理器(Autoload)
你是资深 Godot 4 游戏开发者。请为我的 2D 横版游戏写一个存档管理器 SaveManager(Autoload 单例):
1. 功能:save_game() 遍历 "Persist" 分组,调用每个节点的 save() 方法,用 JSON.stringify 逐行写入 user://savegame.save;load_game() 先检查文件存在、清空旧 Persist 节点,再逐行解析并重建节点(用 scene_file_path 实例化、按记录的 parent 挂载、跳过 filename/parent 字段后 set 属性);另有 delete_save() 和 has_save()。
2. 要求:全部用 GDScript,注释写中文,容错处理(文件不存在、JSON 解析失败要打印错误并跳过该行)。
3. 输出:直接给出完整可运行的 .gd 代码,并告诉我如何在 Project Settings 里注册为 Autoload。
用法:复制即用。跑通后再自己加「存档槽位」「时间戳」「版本号字段」等扩展。
提示词 2:把现有节点改造成可存档
你是 Godot 4 专家。我有个 Player 节点(CharacterBody2D),已有变量:position、health、max_health、inventory(Array[Dictionary])、unlocked_skills(Array[String])。请:
1. 给我完整的 save() 方法:返回包含 filename、parent、坐标(拆成 pos_x/pos_y)、health、inventory、unlocked_skills 的字典;inventory 里的 Dictionary 可能含 Vector2,请说明如何转换。
2. 给我对应的 load_data(data: Dictionary) 方法:把 data 里的值还原到节点,并说明在 main 场景里读档后应如何调用。
3. 提醒我:要把该节点加入 "Persist" 组,以及读档前需要做什么清场操作。
输出格式:代码块 + 分步骤说明。
用法:每做一个新系统(背包、技能、成就),把「变量清单」喂给它,让 DeepSeek 自动补齐存档代码,比自己手写快 10 倍。
提示词 3:实现自动存档 + 存档代码评审
你是 Godot 4 资深架构师。我已有 SaveManager(Autoload)。请设计自动存档方案并给我代码:
1. 自动存档触发点:每 60 秒定时存一次(不卡主线程,用 Timer);玩家进入新关卡/区域时存;玩家死亡或关键剧情节点后存。
2. 存档写盘用异步方式(Thread 或把 FileAccess 操作放进 defer 的队列),避免瞬间卡顿。
3. 最后请以资深评审视角,列出我这段自动存档设计里最容易翻车的 3 个坑(如:存档写到一半游戏退出导致文件损坏怎么办、多个触发点同时触发怎么防重入),并给出修复代码。
用法:直接把输出贴进项目。这条提示词的「评审」环节特别值——让 DeepSeek 当 code review 帮你查竞态和边界条件。
学习心得
离「能上架 Steam」又近一步:存档是玩家体验的信任基石,也是做 demo 拿愿望单的加分项。今天最大的收获是官方「分组 + 每节点自存」的设计——它把「存档」从一坨全局代码变成每个节点的小职责,特别适合配合 DeepSeek 逐模块生成:每个新系统都让 AI 补一对 save()/load(),存档能力就跟着系统一起长出来,不用最后统一补课。
明日关注
- 用 DeepSeek 生成 SaveManager 并跑通「存档 → 重启游戏 → 读档」闭环
- 尝试把背包里的 Vector2 数据正确序列化(JSON 转换练习)
- 研究 Godot 设置存档加密:
save_encrypted_pass()何时该用
本文由自动化学习流水线每日生成,坚持即学即记。
评论