背景:UGC 关卡编辑器不是一个孤立功能
让玩家自己搭关卡很有吸引力:摆平台、放敌人、设计机关、分享给好友。Godot 的场景系统看起来天然适合编辑器,节点拖一拖就能组成玩法。但面向玩家的 UGC 编辑器不能直接暴露真实场景树。玩家可能摆出无法通关的关卡,放太多对象拖垮性能,引用不存在资源,甚至通过配置触发危险脚本。我们做原型时曾直接把编辑器数据存成 PackedScene,结果版本升级后旧关卡大量加载失败。后来改成受控数据格式和运行时沙盒,才稳定下来。
UGC 的核心不是摆放,而是约束。客户端要定义玩家能使用哪些对象、每种对象有哪些参数、总预算多少、如何验证可玩性、旧版本如何迁移、运行时如何隔离脚本能力。Godot 场景树适合作为运行时实例,但玩家存档最好是稳定的关卡数据,而不是任意节点序列。
flowchart TD
A["玩家编辑操作"] --> B["EditorModel: 受控关卡数据"]
B --> C["对象白名单与参数校验"]
C --> D["预算检查: 数量/区域/性能"]
D --> E["可玩性检查: 起点/终点/路径"]
E --> F["保存 Level JSON/Resource"]
F --> G["运行时 Builder 实例化白名单场景"]
G --> H["Sandbox Scene 隔离执行"]
存数据,不存任意场景树
玩家关卡保存为 LevelData:对象类型、位置、旋转、参数、连接关系、版本。运行时 Builder 根据对象类型从白名单实例化 Godot 场景。不要保存任意 PackedScene 或脚本路径。这样版本升级时,可以迁移 LevelData,再用新的对象模板构建。若旧模板路径变了,只要类型 ID 保持兼容,玩家关卡仍能打开。
落地时不要只把这一点写成口头规范,最好把它变成代码入口、配置字段或调试面板。负责实现的人需要说明它依赖哪些 Godot 节点、资源或平台能力,失败时如何降级,日志里能看到哪些字段,QA 应该怎样构造复现样本。UGC 关卡编辑器相关问题通常不会在第一版立刻暴露,而是在内容量增加、设备差异扩大、运营活动叠加后变成偶现缺陷。提前把这些检查点固化下来,后续迭代会轻很多。
对象白名单是安全边界
编辑器只允许放置白名单对象,例如 platform_basic、enemy_slime、door_switch、coin、checkpoint。每个对象声明可编辑参数范围:速度 0 到 5,数量不超过 20,文本长度不超过 40。玩家不能输入脚本路径、资源路径或任意表达式。白名单既保护安全,也保护性能和玩法一致性。新增对象时,程序和策划一起定义参数、预算和校验规则。
落地时不要只把这一点写成口头规范,最好把它变成代码入口、配置字段或调试面板。负责实现的人需要说明它依赖哪些 Godot 节点、资源或平台能力,失败时如何降级,日志里能看到哪些字段,QA 应该怎样构造复现样本。UGC 关卡编辑器相关问题通常不会在第一版立刻暴露,而是在内容量增加、设备差异扩大、运营活动叠加后变成偶现缺陷。提前把这些检查点固化下来,后续迭代会轻很多。
预算要实时反馈
玩家摆太多粒子、敌人或机关,低端设备会崩。编辑器应显示预算条:对象数、敌人数、动态光源、物理机关、预计难度或复杂度。超过软预算时警告,超过硬预算时禁止发布。预算不是为了限制创造,而是让玩家知道关卡能否被别人顺利游玩。不同平台可以有不同发布预算。
落地时不要只把这一点写成口头规范,最好把它变成代码入口、配置字段或调试面板。负责实现的人需要说明它依赖哪些 Godot 节点、资源或平台能力,失败时如何降级,日志里能看到哪些字段,QA 应该怎样构造复现样本。UGC 关卡编辑器相关问题通常不会在第一版立刻暴露,而是在内容量增加、设备差异扩大、运营活动叠加后变成偶现缺陷。提前把这些检查点固化下来,后续迭代会轻很多。
可玩性检查要分层
自动证明关卡可通很难,但可以做基础检查:必须有起点和终点,终点可达,关键门有开关,敌人刷新点不堵出生点,奖励不在非法区域。平台跳跃可以用简化碰撞和路径采样做粗检查,不必完全模拟高手操作。检查结果给玩家具体提示,例如“终点和起点之间没有可行路径”,而不是笼统失败。
落地时不要只把这一点写成口头规范,最好把它变成代码入口、配置字段或调试面板。负责实现的人需要说明它依赖哪些 Godot 节点、资源或平台能力,失败时如何降级,日志里能看到哪些字段,QA 应该怎样构造复现样本。UGC 关卡编辑器相关问题通常不会在第一版立刻暴露,而是在内容量增加、设备差异扩大、运营活动叠加后变成偶现缺陷。提前把这些检查点固化下来,后续迭代会轻很多。
预览运行要隔离
玩家点击试玩时,关卡在 Sandbox Scene 中构建。Sandbox 只加载白名单对象,不允许对象访问全局敏感服务,例如支付、账号、真实存档。试玩结束后完整释放 Sandbox,回到编辑器。这样玩家关卡里的机关不会影响大厅状态。即使对象脚本是项目内置的,也要限制它们在 UGC 模式下能做什么。
落地时不要只把这一点写成口头规范,最好把它变成代码入口、配置字段或调试面板。负责实现的人需要说明它依赖哪些 Godot 节点、资源或平台能力,失败时如何降级,日志里能看到哪些字段,QA 应该怎样构造复现样本。UGC 关卡编辑器相关问题通常不会在第一版立刻暴露,而是在内容量增加、设备差异扩大、运营活动叠加后变成偶现缺陷。提前把这些检查点固化下来,后续迭代会轻很多。
版本迁移要从第一天设计
UGC 一旦发布,旧关卡就是玩家资产。对象参数改名、尺寸变化、机关逻辑调整都可能影响旧关卡。LevelData 带 schema_version,每次升级写迁移器。对象模板也带 object_version,Builder 能根据旧参数补默认值。删除对象要谨慎,可以保留 deprecated 模板用于加载旧关卡,只在编辑器里不再提供。
落地时不要只把这一点写成口头规范,最好把它变成代码入口、配置字段或调试面板。负责实现的人需要说明它依赖哪些 Godot 节点、资源或平台能力,失败时如何降级,日志里能看到哪些字段,QA 应该怎样构造复现样本。UGC 关卡编辑器相关问题通常不会在第一版立刻暴露,而是在内容量增加、设备差异扩大、运营活动叠加后变成偶现缺陷。提前把这些检查点固化下来,后续迭代会轻很多。
分享前做发布校验
本地能玩不代表能发布。发布前再次跑校验、预算、敏感词、资源引用和版本兼容。生成关卡摘要:名称、作者、对象统计、封面图、目标客户端版本。服务端也应重复关键校验,不能完全信客户端。客户端校验负责即时反馈,服务端校验负责平台安全。
落地时不要只把这一点写成口头规范,最好把它变成代码入口、配置字段或调试面板。负责实现的人需要说明它依赖哪些 Godot 节点、资源或平台能力,失败时如何降级,日志里能看到哪些字段,QA 应该怎样构造复现样本。UGC 关卡编辑器相关问题通常不会在第一版立刻暴露,而是在内容量增加、设备差异扩大、运营活动叠加后变成偶现缺陷。提前把这些检查点固化下来,后续迭代会轻很多。
编辑器体验细节
撤销重做、网格吸附、对象搜索、批量选择、复制粘贴、对齐、测试入口、错误定位,这些决定玩家是否愿意创作。Godot 做工具很方便,但面向玩家的工具要比内部编辑器更克制。不要把 Inspector 原样暴露给玩家,而是设计少量明确控件。UGC 成功的关键是让创作自由发生在安全边界内。
落地时不要只把这一点写成口头规范,最好把它变成代码入口、配置字段或调试面板。负责实现的人需要说明它依赖哪些 Godot 节点、资源或平台能力,失败时如何降级,日志里能看到哪些字段,QA 应该怎样构造复现样本。UGC 关卡编辑器相关问题通常不会在第一版立刻暴露,而是在内容量增加、设备差异扩大、运营活动叠加后变成偶现缺陷。提前把这些检查点固化下来,后续迭代会轻很多。
撤销重做基于命令,而不是快照全量场景
UGC 编辑器必须有撤销重做。简单做法是每次操作保存完整 LevelData,但大关卡会很重,也难以合并。我们更推荐命令模式:AddObject、MoveObject、ChangeProperty、DeleteObject、ConnectSwitch。每个命令知道 do 和 undo。命令同时更新 EditorModel 和视图。保存时再序列化完整 LevelData。
命令模式还方便多人协作或未来云端编辑,因为每个操作都有语义。即使不做复杂协作,命令日志也能帮助排查玩家编辑器 bug:最后几步做了什么,为什么关卡进入非法状态。
编辑器和运行时使用同一套构建器
预览、试玩、发布前校验都应使用同一个 LevelBuilder。不要编辑器里一套显示逻辑,运行时另一套实例化逻辑。否则玩家编辑时看起来正常,发布后对象行为不同。Builder 可以根据模式选择不同细节:编辑模式显示辅助框和控制点,试玩模式启用真实碰撞和 AI,但对象来源和参数解释必须一致。
这也方便测试。给一份 LevelData,自动构建编辑视图和运行视图,检查对象数量、参数和连接关系一致。UGC bug 很多来自两个视图不一致,统一构建器能减少这类问题。
发布后的关卡要可诊断
玩家分享关卡后,别人打不开或卡住,开发需要诊断。LevelData 里保留摘要:对象统计、预算结果、校验版本、发布客户端版本。加载失败日志带关卡 ID 和失败对象 ID。若某个对象模板导致大量玩家关卡失败,可以通过 ID 聚合。UGC 是内容平台,诊断能力越早做越好。
接口约定
UGC 对象模板要像产品化 API:type_id、version、display_name、icon、editable_properties、budget_cost、build_scene、validator。编辑器根据模板生成属性面板,Builder 根据模板实例化运行对象,Validator 根据模板检查参数。新增对象必须补齐这些字段,否则不能进入玩家编辑器。
自动化测试可以读取所有模板,生成最小对象,保存为 LevelData,再构建、校验、销毁。还可以准备几份旧版本关卡样本,确保迁移后仍能加载。UGC 一旦开放给玩家,旧内容就是资产,模板测试和迁移测试不能省。
上线前的复盘方式
这类系统上线前,我会要求团队做一次小型复盘,而不是只看功能是否完成。复盘内容包括:这个能力的唯一入口在哪里,哪些页面或玩法已经接入,哪些路径仍然是旧实现;失败时玩家看到什么,日志能不能说明原因;低端设备、弱网、切后台、快速重复操作会不会改变结果;如果运营或美术改了资源,客户端有没有校验和降级。把这些问题逐条过一遍,通常能提前发现很多“不是 bug 但会上线出事”的边界。
复盘还要留下可执行资产。比如一个测试场景、一组假数据、一个调试开关、一份检查脚本。只写会议结论没有用,下一次迭代很快会忘。Godot 项目迭代速度快,越是快,越需要把经验沉淀成工具。否则每个版本都靠同一批人记忆项目细节,团队规模稍微扩大就会失控。
线上观测指标
上线后至少记录三类指标:使用次数、失败次数和耗时或资源占用。使用次数说明功能是否真的被走到;失败次数说明降级路径是否健康;耗时和资源占用说明它是否给性能带来压力。指标不需要一开始很复杂,但必须能按客户端版本、资源版本和设备档位拆分。很多 Godot 客户端问题只在特定设备或特定资源包上出现,没有这些维度,日志量再大也难定位。
当指标异常时,要能快速关闭或降级。功能入口、资源变体、表现强度、调试采样率都应有安全开关。工程系统成熟的标志,不是永远不出问题,而是出问题时能定位、能止血、能恢复。Godot 内置关卡编辑器:UGC 创作、校验与运行时沙盒 这样的能力尤其如此,它连接了多个子系统,任何一个边界没守住,都可能表现成玩家端的偶现体验问题。
结语
Godot 客户端开发里,真正拉开项目质量差距的往往不是某个 API 的使用技巧,而是系统边界是否清楚。输入、动画、渲染、音频、UGC、富文本、网络、奖励和资源缓存都可以先做一个能跑的版本,但如果没有统一入口、状态机、调试面板和失败路径,后续内容量一上来就会变成难以维护的偶现问题。
我更倾向于把这些能力当作小型基础设施来做:先定义语义接口,再限定资源和数据边界,然后给开发和 QA 足够的观察工具。这样每次新增需求都不是往场景树里再塞一段临时代码,而是在已有规则里扩展一个新用例。项目长期运行时,这种朴素的工程秩序比一次性的聪明写法更可靠。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。