编辑器里填的数据,不等于玩家存档
Godot 编辑器很适合让开发者在场景里填数据:怪物出生点、宝箱掉落、NPC 对话、触发区域、相机边界、背景音乐、任务 ID。导出变量和 Resource 让这些数据很容易被内容人员编辑。问题是,很多项目没有区分“编辑器创作数据”和“运行时玩家状态”。最后场景里既有默认宝箱配置,又有玩家是否打开过宝箱的状态,越改越乱。
客户端数据通常分三类:只读配置、场景创作数据、玩家存档。只读配置随版本发布,场景创作数据描述关卡初始状态,玩家存档记录玩家改变了什么。三者边界清楚,游戏才能稳定更新和兼容旧档。
flowchart TD
A[编辑器场景/Resource] --> B[构建前校验]
B --> C[运行时只读关卡数据]
C --> D[场景实例化]
E[玩家存档] --> F[状态覆盖层]
F --> D
D --> G[运行时对象]
G --> H[玩家行为变化]
H --> E
Resource 适合表达可复用配置
Godot Resource 很适合配置道具、敌人、技能、关卡参数。它可序列化、可在 Inspector 里编辑,也能被多个场景引用。比如 EnemyConfig 里有血量、速度、掉落表;ChestConfig 里有奖励池、是否可重复;DialogueConfig 里有对话节点。
Resource 的优势是复用和版本管理。多个怪物实例引用同一个配置,平衡调整只改一处。缺点是运行时不要直接修改共享 Resource。若某个怪物受 Buff 改了速度,应该改实例状态,不要改 EnemyConfig,否则所有同配置怪物都变。
可以把 Resource 分成只读配置和运行时状态对象。只读配置加载后不变,状态对象由场景实例或存档创建。代码评审要特别关注是否误改了配置资源。
场景元数据要可校验
关卡场景里的导出变量很方便,但也容易漏填。宝箱缺 config,传送门目标为空,NPC 任务 ID 写错,出生点放在墙里。编辑器不会自动知道这些业务规则,项目要做校验工具。
校验可以在编辑器按钮、提交前脚本、CI 构建中运行。规则包括:必填字段不为空,引用资源存在,ID 唯一,目标场景可加载,区域不重叠,出生点在可走区域。错误报告要定位到具体场景和节点。
运行时也要有保护。遇到缺配置的非关键对象,可以隐藏并上报;关键对象缺配置,则阻止进入关卡并提示资源异常。不要让空引用在玩家操作时才崩。
玩家状态用覆盖层表达
玩家打开宝箱、击败 Boss、解锁门、移动机关,这些不应该写回原始场景文件。导出包里的场景是只读初始状态。玩家存档记录的是覆盖层:对象 ID -> 状态变化。加载关卡时,先实例化初始场景,再应用存档覆盖。
对象 ID 必须稳定。不要用节点路径或列表索引作为长期 ID,因为关卡编辑会改层级和顺序。可以给可存档对象一个导出字段 persistent_id,构建前校验唯一性。新增对象生成新 ID,删除对象保留迁移规则。
覆盖层也有版本问题。关卡更新后,旧存档里记录的对象可能不存在。加载时要忽略已删除对象的状态,或执行迁移。不要因为一个旧宝箱 ID 找不到,就让整个存档失败。
运行时修改和编辑器修改分开
Godot 的 PackedScene 实例化后,节点属性可以随意改。但要明确哪些修改只是运行时表现,哪些需要进存档。比如门打开状态需要存,门打开动画播放进度不一定需要存;敌人死亡需要存,死亡粒子是否播完不需要。
可以给可存档对象定义接口:get_save_state()、apply_save_state(data)。存档系统只通过接口读写,不扫描所有节点属性。这样每个对象自己决定哪些状态有意义。
编辑器数据更新也要小心。策划把门默认改为打开,新玩家应该看到打开;老玩家存档里如果这扇门已关闭,覆盖层是否继续生效?这需要版本规则。配置变化和玩家状态冲突时,项目要有决策。
导出前生成运行时索引
为了加快加载,可以在构建前从场景元数据生成运行时索引:关卡对象表、传送点表、可存档对象 ID 表、资源依赖表。运行时读取索引,比在场景树里到处扫描更稳定。
索引生成要可重复,并和源场景一致。构建前检查源场景和生成索引是否同步。若团队提交生成文件,就要防止忘记更新;若构建机生成,就要保证工具可用。
小结
Godot 编辑器数据和玩家存档必须分层。Resource 表达只读配置,场景元数据描述初始关卡,玩家存档作为覆盖层应用,稳定对象 ID 连接两者,校验工具保证内容正确。这样关卡能持续迭代,玩家进度也不会被场景改动轻易破坏。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
我会给每个可存档节点在编辑器里显示 persistent_id 和当前校验状态。内容人员复制节点时,如果 ID 重复马上变红,比等到存档覆盖错对象后再查要安全得多。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。