INTERACTIVE PAPER DECK26 页交互图解:从 Godot 的物理帧到 mutant 校准 展开阅读
一局游戏结束时看起来正确,不代表它在中途没有违规则。GameLogicBench 把这个差别变成了可重复执行的测试:Agent 在 Godot 项目里补上玩法逻辑;隐藏的判分程序(judge)选择不同场景和随机种子,在每一个 simulation tick(仿真更新步)检查位置、时间、资源和事件顺序是否始终满足规则。
先认识 Godot:为什么这篇论文总在谈 Scene、Node 和 tick
Godot 用 scene(场景)和 node tree(节点树)组织游戏。角色可以是一个 CharacterBody2D 节点,下面挂着碰撞体、贴图和相机;关卡 scene 再把这棵角色树实例化进来。脚本通常附着在某个 node 上,通过引擎回调推进状态,再用 signal(信号)通知其他 node。Godot 官方文档把 node 称为游戏的基本构件,把 scene 描述为可保存、可重复实例化的 node tree。
_physics_process(delta) 中;论文的 judge 以固定 timestep 运行并逐 tick 断言。health_changed,UI 订阅后更新血条,不需要把 UI 写死进角色脚本。下面是一段可以挂到 CharacterBody2D 的 Godot 4.x 脚本。它故意很小,却包含了这篇论文关心的三类东西:每个物理帧读取输入、更新速度和位置;生命值变化时发出事件;外部代码能从公开状态读取行为结果。
player.gd · Godot 4.x
1 | extends CharacterBody2D |
如果只在最后读取 health == 3,就看不出它是否曾被错误地扣到 2、又被另一段代码补回 3。逐 tick 评测会检查完整轨迹:每一步发生了什么、何时发生、不同系统是否同时遵守约束。这正是 GameLogicBench 选择游戏逻辑作为研究对象的原因。
背景资料来自 Godot 4.4 官方文档的 Nodes and Scenes 与 Using signals;上面的示例是为本文写的最小示意,不是论文任务代码。
Q1: 这篇论文试图解决什么问题?
很多游戏规则约束的不是终点,而是到达终点的过程。设想一个资源系统:第 30 帧扣掉 10 单位资源,第 31 帧又错误地多加 10;结束时余额与正确实现相同。如果评测只看最后一帧,它会把这段错误轨迹当成正确。类似问题也会出现在 cooldown(冷却时间)、移动碰撞、跳跃落地、状态机切换和同一帧内的多个请求中。
现有游戏开发 benchmark 常见三种判法:复播一条固定示例、看最终视频或截图、让另一个模型操作并打分。它们各有用处,但没有同时提供下面三项能力:
- judge 自己选择多个合法场景,而不是只看 Agent 已见过的 preview;
- 在运行过程的多个 tick 读取状态和事件历史,而不是只核对最后结果;
- 判分路径中没有语言模型,使同一提交、场景与 seed 可以得到完全一致的 verdict(判定)。
GameLogicBench 不评价“这款游戏好不好玩”,也不评价美术和关卡内容。它只检查一件事:Agent 补上的 gameplay logic,能否在多种合法运行条件下始终遵守预先写好的规则。
Q2: 有哪些相关研究?
先区分两类任务。GameCraft-Bench、WebGameBench 等评测完整游戏能否生成、能否玩、视觉是否符合要求;GameLogicBench 给 Agent 一个可运行的 Godot 项目,只让它补上指定机制,再用 engine assertion(引擎内断言)验收。GameLogicBench 的覆盖面较窄,但每个 verdict 都能复现,也能定位运行途中发生的违规。
| Benchmark | 产物 / 任务 | 判定方式 | 确定性 | 跨 tick | 每题 scenario |
|---|---|---|---|---|---|
| SWE-bench | 软件仓库 patch | Unit tests | 是 | 不适用 | 不适用 |
| Terminal-Bench 2.0 | 终端任务,生成或 patch | Unit tests | 是 | 不适用 | 不适用 |
| WebCompass | Chromium / JS,生成或 patch | Agent judge | 否 | 不适用 | 1 |
| WebGameBench | 浏览器游戏,从头生成 | Agent judge | 否 | 否 | 1 |
| GameDevBench | Godot / GDScript,真实项目 patch | Test scripts | 是 | 部分任务 | 1 |
| JAMER | Godot / GDScript,生成或 patch | Engine verify | 是 | 否 | 1 |
| GameCraft-Bench | 完整 Godot 游戏,从头生成 | VLM judge | 否 | 否 | 1 |
| GameEngineBench | UE5 / C++,真实项目 patch | Tests + LLM judge | 否 | 部分任务 | 1 |
| AutoUE | UE5 / C++,从头生成 | LLM judge | 否 | 否 | 1 |
| GameLogicBench | Godot / GDScript,生成项目或真实项目 patch | Engine assertions | 是 | 全部任务 | 2–12 |
这张比较表需要保留一个边界:它比较的是作者选取的、会直接判断运行中产物的 benchmark,不是所有代码 benchmark 的完整综述;“本文唯一同时具备三项性质”也只在这组对照里成立。
Q3: 论文如何解决这个问题?
三个 tier 描述集成范围
- Atom:在为 benchmark 新做的最小游戏里隔离一个机制,例如敌人寻路;21 题。
- Combo:仍是最小游戏,但几个机制会在时间、空间或并发调用中互相影响,例如巡逻、视线、跳跃和追逐同时成立;28 题。
- Repo:在真实开源 Godot 项目中补回一个组件,需要读懂现有调用方、动画、相机、数据资源和共享状态;23 题。
三类任务不是难度标签,而是 integration scope(集成范围)。论文的数据确实显示 Repo 更难,但不能反过来把所有 Repo 都理解成“难题”,也不能把 Atom 理解成简单算法题。
Agent 看见什么,judge 又知道什么
每题给 Agent 一个任务说明和 Godot 项目,其中有可运行的 preview 与调试输出。Agent 知道功能规则、允许修改的文件、接口和“场景每次会重新生成”等事实;它只看到一个公开的 baseline scenario(基线场景)。隐藏 judge 使用同一接口,但会选择其他布局、输入序列、调用顺序和 seed。隐藏的是具体测试实例与 judge 代码,不是另外一套规则。
从候选机制到可计分任务
- 01 · SOURCING从开源 Godot 项目、已发布游戏和功能能力 taxonomy 收集候选机制。
- 02 · SCREENING三名人工标注者判断规则能否写成唯一值、合法事件顺序或守恒量。
- 03 · BUILDAgent 依次做可行性试验、blueprint 和可执行 task / judge。
- 04 · CALIBRATE用正确解、等价正确解、naive 解和 mutants 检查 judge。
- 05 · REVIEW独立 review agent 复跑,再由三名人工标注者录取、退修或淘汰。
大约 200 个候选想法中,122 个走完构建、校准和 Agent review,最终 72 个进入 benchmark。标注者先判断一种行为能否客观测量,Agent 再把它做成项目和 judge,随后用反例检查 judge 有没有漏掉必须满足的能力。这里的“标注”包含了一整套可执行测试的构建与校准,不是给样本贴一个静态标签。
为什么要同时放“两个正确解”和“多个错误解”
一套测试只跑过 reference solution,只能说明它能接受一种写法;它可能把变量名、控制流程或某个偶然数值当成正确性的必要条件。GameLogicBench 因此设置四类 calibration artifact(校准样例):
| 校准样例 | 必须得到的结果 | 它在检查什么 |
|---|---|---|
| proper solution | 所有 scenario 与 seed 都通过,并离容差边界有余量 | 任务确实可解 |
| behavior-preserving control | 通过 | 另一种内部实现只要可观察行为相同,也应被接受 |
| naive solution | 失败 | 常见的“看似能跑”实现不能蒙混过关 |
| single-capability mutant | 至少被一个专门 scenario 抓住 | 每次只删掉一项能力,验证 judge 对这项缺失确实敏感 |
Mutant 校准确实找到了会改变榜单结果的漏洞。作者在 36 题审计子集上检查“未经过 mutant 修补”的 criterion(判定规则):666 个 mutant 中有 127 个错误实现通过,暴露出 19 道任务里的 24 个缺失检查。修补后,9 个模型 × 2 个 scaffold 的历史提交里,有 3 个结果从 PASS 改成 FAIL,涉及 2 道任务和 3 个模型;正确解与行为等价的 control 仍然通过。
Judge 如何把 scenario、seed 和 tick 串起来
求解和判分运行在两个独立、断网的容器中。judge 拿到 Agent 工作目录的副本和冻结的测试文件;对每个 scenario 与 seed,它用固定 timestep 启动游戏。scenario 定义测试结构,例如“从相反方向接近台阶,前方再放一堵过高的墙”;seed 再为台阶高度、距离或输入时序等参数取具体数值,于是形成一个 test case。
官方仓库目前公开的是评测 harness,而不是 72 道题各自的 judge。能直接核查到的隔离机制是:harness 按 game → solution → judge 的顺序组装项目,最后覆盖冻结的 judge 文件;隐藏 seed 通过命令行参数传入,不写进 Agent 可见的项目目录。论文链接的 GameLogicBench-Tasks 仓库在本文核查时返回 404,所以 task-specific checker 的具体断言仍然无法公开审计。
下面的代码只把论文协议写成伪代码,帮助读者看清逐 tick 断言的粒度;它不是官方 task-specific judge 源码。
1 | for scenario in hidden_scenarios: |
judge 还会改变接口允许的调用计划,包括 concurrent calls(同一阶段出现多个调用)、re-entry(一次流程尚未退出又再次进入)和 stretched time base(拉长时间步尺度)。这些变化只影响执行条件,功能要求不变。断言读取运行状态(runtime state)与事件历史(event history),不读 Agent 的源码,也不接受 Agent 自己上报“我通过了”。
判分按四层汇总。每个 test case 先得到二元 PASS/FAIL;一个 scenario 只有在它的全部 seed 都通过时才算 strict pass;一项 task 的所有计分 case 都通过,才记为 solved;主表的 solve rate = solved tasks / 72。没有可判定 solution 的运行和不可用的 computation 都算失败,不会从分母中剔除。Figure 8 的能力分数另按 scenario 统计:先要求一个 scenario 的所有 seed 通过,再在带有相应能力标签的 scenario 上汇总。一个 scenario 可以有多个能力标签,所以七类能力的分母不能相加。
真实 case 1:Atom / Enemy Navigation
一个方向向量,为什么也值得逐帧测
- 任务。Agent 只实现
res://logic/controller.gd中的decide(state) -> Vector2。返回值表示本物理帧的移动方向。 - 公开输入。
state给出当前位置、目标、角色半径、物理世界、navigation map、dt和累计时间;preview 展示一张固定示例地图。 - 隐藏变化。墙、门洞、起点和终点会程序化重排;不同 seed 生成不同数值实例。
- 逐帧观察。每一帧都要确认圆形角色没有碰墙;结束前还要确认它在时限内抵达终点。
这个 case 也说明了 terminal-only(只看最终状态)的盲点:controller 可以先穿墙、再到终点。终局满足“抵达”,运行轨迹已经违反碰撞规则。
真实 case 2:Combo / Platform Guard
同一个 controller 同时承担巡逻、视线、跳跃与返回
- 任务。
decide(state)每帧返回{"move": float, "jump": bool, "chasing": int}。 - 场景。两块平台之间有落差和深坑,tower(障碍塔)会遮挡视线;平台宽度、gap、访客出现时间和出生点都会变化。
- 动作。安静时覆盖 home platform(初始驻守平台)至少 34% 的可行走范围;看见 intruder(入侵者)后接近到 130 units 内,而且必须站在地面上;失去目标后 6 秒内返回 home。
- 观察。judge 逐帧检查是否坠落、是否把墙后的目标谎报为
chasing、跳跃轨迹是否真正落到目标旁、离家是否超时。
这里的 34%、130 units 和 6 秒都来自公开 brief,不是本文自行推测。真正的隐藏 scenario 与 judge 源码没有随论文主仓库公开,因此本文不会把示意伪代码冒充官方实现。
真实 case 3:Repo / AMSG Character Movement
Repo 示例要求 Agent 在一个 MIT 许可的第三人称角色 kit 中重建 CharacterMovementComponent.gd。它要保持原有类、导出属性、状态字段和方法签名,让动画、相机、控制器和数据资源继续读取同一个组件。功能包括三种 gait(步行、奔跑、冲刺)、松开输入后的减速、蹲下与头顶阻挡、任意方向的台阶攀爬、落地后跳跃,以及离地 0.1 秒后才确认下落。
论文正文给出了一组具体的公开/隐藏差异:preview 让角色从 +X 方向走上一个合法台阶;某个计分 scenario 改为从 -X 接近,并在路径更远处放一堵超过最大台阶高度的墙。正确实现必须爬上台阶,但不能把后面的高墙也当成台阶。judge 会检查角色能否从不同方向识别可攀爬高度,同时维持整套 character rig(角色控制系统)的接口和状态约束。
规模与覆盖
每道任务有 2–12 个 scenario、10–38 个 test case。Repo 平均含 209 个游戏文件、17,599 行游戏代码;Atom 与 Combo 平均都只有 5 个游戏文件,代码量分别为 316 与 421 行。Repo 题需要先找到真正控制行为的代码位置,再维持周边系统依赖的接口和共享状态。
Q4: 做了哪些实验?效果如何?
实验设置
作者测试了 20 个 model + scaffold 组合。scaffold 是承载模型、提供读文件、改代码和执行命令等工具的 Agent 框架;论文使用 Claude Code 2.1.177、Codex 0.144.1 和 OpenCode 1.17.18。所有配置使用 effort=high,每题上限 3,600 秒,求解与判分都运行 Godot 4.4。主表中的每个组合只运行一次,因此它是 observed result(观察到的一次结果),不能当作稳定的模型能力估计。
结果 1:集成范围越大,所有模型都明显掉分
固定 Claude Code 后,12 个模型都从 Atom 到 Combo、再到 Repo 下降。聚合 solve rate 是 45.2% → 31.5% → 21.7%。与此同时,平均 turn 增加,Agent 启动 Godot 的次数更多,重新选择 preview seed 的 session 比例从 Atom 的 38.9% 上升到 Repo 的 55.8%。Agent 确实在尝试运行反馈,但项目范围扩大后,它们仍然难以同时满足全部规则。
结果 2:多数失败项目能运行,但机制行为不合格
作者把失败 scenario 分成三类:74.3% 是 mechanism failure(机制失败),即提交可以运行和判分,但行为违反了契约;17.2% 没有可判定的提交;8.5% 的求解过程本身不可用。把全部配置和 scenario 合并统计后,Engine contract 的 strict pass rate 为 88.75%,在 20 个配置中的 19 个排第一;Commitment、Spatial 与 Timing 只有 53.4%–58.57%,在 20 个配置中的 17 个排倒数三位。
结果 3:删掉逐帧检查或隐藏场景,分数会被大幅高估
在 36 题审计集上,作者固定同一批 666 个 mutant 和 20 个配置产生的 720 份提交,分别做两种消融:
- Terminal-only:保留所有 scenario 与 seed,但只看终局。236/666 个 mutant 逃过检查,34/36 题至少漏掉一个 mutant;原本失败的提交中有 64/488 被误判为通过,平均 solve rate 增加 8.9 个百分点。
- Preview-only:保留完整逐 tick criterion,但只跑公开 preview。508/666 个 mutant 逃过检查,36/36 题都有漏网;418/488 个原本失败的提交被误判为通过,平均 solve rate 增加 58.1 个百分点。
结果 4:同一模型换一个 scaffold,表现和工具使用都会变
Qwen-3.8-Max 在三个 scaffold 下分别得到 44.44%、26.39% 和 29.17%;GPT-5.6-Sol 则是 41.67%、34.72% 和 34.72%。Codex 平均 turn 最少,Qwen-3.8-Max 与 GPT-5.6-Sol 在 Codex 下的各分位 tool call 也更少,但“交互少”并不稳定对应更高或更低的分数。模型与框架是共同起作用的系统,单独按模型名字排榜会掩盖这个差异。
结果 5:开放网络会让 Repo 题变成源码检索题
作者另外让五个配置在 Repo 题上开放网络,并审阅 URL、查询和命令轨迹。四个配置确实找到了上游源码,而且人工检查发现了直接复用;这四个配置的 Repo 成绩都高于各自的断网版本。最大增幅是 Kimi-K3 + Claude Code,从 3 题增加到 12 题(+9)。
完整原始图表索引论文的 9 张 Figure 与 12 张编号 Table 均有记录


















Q5: 有哪些值得继续探索的问题?
第一,扩大可测范围。当前 benchmark 主动排除了依赖审美和体验的规则,只覆盖能写成确定性状态断言的 gameplay logic。美术质量、节奏、关卡趣味和完整玩家体验仍要由其他 benchmark 补上;未来也可以加入更长交互和更多 runtime signal,但前提是保持 verdict 可重复。
第二,扩展引擎与多人系统。现在只测 Godot 4.4 + GDScript,单容器 harness 也不覆盖网络同步。迁移到 Unity、Unreal 或多容器 client–server 测试时,如何继续控制时钟、异步任务和网络事件顺序,是直接而困难的后续问题。
第三,增加统计稳定性。主表每个 model + scaffold 组合只有一次完整运行。论文另对 Qwen-3.8-Max、GLM-5.2、Kimi-K3 在 Claude Code 下各跑三次:单次分数的标准差相当于约 2–3 道题,而 pass@3 与 worst@3 相差 26.39–36.11 个百分点。“某配置这次解出 38/72”只能表示这一次运行的结果,不能当作稳定能力上限。
第四,补足公开复现链。论文主页提供了评测框架仓库,README 指向单独的 GameLogicBench-Tasks 仓库;截至本文核查时间(2026-09-22),该链接返回 404。因此可以审计 harness 和论文附录中的三个完整 brief,却不能从公开仓库逐项复跑 72 题、隐藏 judge 与 mutant archive。本文中的 task case 只引用论文公开材料;任何等价代码都明确标为示意,而非官方 judge 源码。
第五,把“防污染”当成 benchmark 设计的一部分。Repo 任务来自公开项目;只要模型能联网,测试可能从“理解仓库并实现机制”变成“找到原文件并复制”。未来发布任务时,需要在可审计性与防直接检索之间做版本化设计,例如保留可公开的构建记录,同时把真正计分的新变体放在受控评测环境中。
Q6: 总结
GameLogicBench 的主要贡献是一条可以反复审计的评测链。人类先筛选能够确定性判断的机制;Agent 辅助构建 task 与 judge;正确解和等价正确解检查测试是否错误地绑定某种实现;naive 解与单能力 mutant 检查测试能否拒绝缺失能力的实现;最终 judge 再用多个 scenario、多个 seed 和逐 tick 状态断言评测提交。
它的实验给出三个直接结论:第一,只看最终状态会漏掉大量中途违规;第二,只测公开 preview 会严重高估成功率;第三,大部分失败项目其实能运行,真正薄弱的是跨帧维持决策、空间和时间约束。最佳单次结果仍只有 52.78%,而 Repo tier 在 Claude Code 下的聚合 solve rate 只有 21.7%。
读这篇论文时也要保留两条边界:它不评价一款游戏是否好玩,只评价指定逻辑是否满足可执行规则;主结果是一次运行的观察值,并且完整任务库当前无法从公开链接取得。把它当作“怎样设计可信的 runtime benchmark”比把它当作模型绝对排名更有收获。
留言