logo

AI做的游戏能玩吗?SWE-Game基准测试的答案,以及用AI把游戏做到“真能玩”的实操方法

作者:很酷cat2026.11.14 09:19浏览量:1

简介:让AI独立做一款游戏,它通常会先搭场景、放角色、做好移动和跳跃,然后故事就卡住了:尖刺看得见但踩上去没反应,通关画面能弹出来但玩家什么都没做就直接触发了。这不是段子,而是上海交大等机构的研究者在系统性测试AI编程能力时真实撞见的现象,结论被写进了 SWE-Game 这个基准:AI生成的游戏离“能玩”还差得远。本文拆解这个基准到底测了什么、为什么难测,并给出一套可落地的工程方法——用 [ERNIE 5.5 Flash Preview 在线体验](https://console.bce.baidu.com/qianfan/onlineTest/chat/ERNIE-5.5-Flash-Preview) 把AI的工作从“生成代码”推进到“生成可被验证的可玩性”。

一、先说结论:AI做游戏的瓶颈不在“写得出”,而在“跑得对”

如果你让AI写一个排序函数,跑一遍单元测试就知道对错。但游戏不一样,它活在玩家的操作里。

一个2D平台跳跃游戏里,“角色碰到尖刺应该死”这件事,没法靠阅读代码片段判断——你必须真的操纵角色走过去撞一下,看它是否死亡、是否扣血、是否回到检查点。研究团队把这个问题讲得很直白: 如果玩家看到通关画面就认为游戏做对了,那也可能是假象,因为游戏可能根本没要求玩家做任何操作,就直接弹出了胜利提示。

所以AI做游戏常见的失败模式,几乎都不是“编译不过”,而是这类语义级的空转:

失败表现 表面现象 真实原因
碰撞体缺失或层级错配 尖刺看得见,踩上去无反应 Area2D/CollisionShape2D 未正确连接信号
胜利条件写反或恒真 一进关卡就弹通关 判定条件未与“收集/到达”状态绑定
状态机缺分支 跳跃后可二段跳、死亡后仍可操作 状态迁移条件不完整
数值与关卡不匹配 跳跃高度不足以通过既定缺口 关卡几何与角色参数未联合校验
假交互 按钮有反馈但无实际逻辑 UI 信号未绑定业务函数

这些问题的共同点是: 只有运行时证据能证伪,静态阅读代码看不出来。

二、SWE-Game 到底测了什么

SWE-Game 的核心矛盾可以概括成一句话:怎么让评测既知道“这个游戏本来应该是什么样”,又能亲自“动手”去验证“它现在到底是什么样”。

为此团队没有凭空出题,而是先造了 41款真正能玩的参考游戏 ,横跨2D与3D,涵盖射击、平台跳跃、解谜、策略、模拟经营等 13种玩法类型 ,全部使用开源引擎 Godot 开发,美术上采用 Kenney 等现成素材包,并走了一套正规的开发流程。这批游戏的作用类似“标准答案”:它们既提供了明确的设计意图,也提供了可运行的参照实现。

在此之前,评测AI做游戏大致有两条路,各有明显短板:

  • 让另一个大模型“看”游戏并打分。 相当于请一位美术老师远程批改他看不到实物的雕塑作业,只能看照片,判断力天然打折,且容易被“看起来像”的表象欺骗。
  • 写死板的单元测试。 能判断某个函数返回值对不对,但抓不住“动态交互中的逻辑是否自洽”这种更接近游戏本质的问题。

同期的 WebGameBench(靠浏览器交互评测网页小游戏)、GameLogicBench(靠引擎内状态断言测试机制)各有侧重,但都没能把 设计意图 与 运行时证据 真正打通。SWE-Game 的价值就在于把这两端接了起来。

对开发者的直接启示是: AI做游戏的质量上限,取决于你有没有给它一套“可玩性验收标准”,以及一条能自动跑出运行时证据的回路。

三、把“可玩性”变成可执行的验收契约

想让AI(以及你自己)判断游戏是否做对,第一步不是写代码,而是把设计意图写成机器可读的契约。建议在动手前先产出一份 playability_contract.json,它同时是代码生成的输入和测试生成的输入。

  1. {
  2. "game_id": "platformer_01",
  3. "engine": "godot-4.3",
  4. "genre": "2D平台跳跃",
  5. "win_condition": "coins_collected == total_coins && player.touch_goal",
  6. "fail_conditions": ["player.touch_spike", "player.position.y > 900"],
  7. "entities": [
  8. {"id": "player", "scene": "res://entities/player.tscn", "max_health": 3},
  9. {"id": "spike", "scene": "res://entities/spike.tscn", "damage": 1}
  10. ],
  11. "acceptance_checks": [
  12. {"id": "AC-01", "desc": "碰到尖刺扣1点生命并进入短暂无敌", "type": "runtime_assert"},
  13. {"id": "AC-02", "desc": "未集齐金币时触碰终点不触发通关", "type": "runtime_assert"},
  14. {"id": "AC-03", "desc": "集齐金币后触碰终点弹出通关画面", "type": "runtime_assert"},
  15. {"id": "AC-04", "desc": "掉出关卡下边界后重生到检查点", "type": "runtime_assert"}
  16. ]
  17. }

有了这份契约,“这游戏做得真不真”就从主观争论变成了 4 条可以自动跑的断言。

四、用 ERNIE 5.5 Flash Preview 搭一条“生成—自测—修复”闭环

ERNIE 5.5 Flash Preview 是文心系列基础模型 ERNIE 5.5 Flash 的预览版本,重点优化了编程、智能体、知识工作与深度研究等方向的能力,支持 1M tokens 长上下文 ,可结合工具调用与 Agent 工作流完成多步骤任务。它属于千帆平台提供的模型服务,平台侧同时提供模型调用、应用编排等工具链。

需要说明的是:它是文本模型,本身不运行 Godot、不渲染画面。真正跑起来需要本地 Godot 可执行文件或 CI 环境配合。它在这套流程里承担的是三件最耗人的事:

  1. 读懂整个项目。 1M 上下文意味着你可以把多个 .gd 脚本、.tscn 场景文本、日志一次性贴进去,让它做跨文件的一致性检查,而不是只看单个函数。
  2. 把契约翻译成测试代码。 从 acceptance_checks 直接生成 Godot 无头测试脚本,覆盖碰撞、状态机、胜负判定。
  3. 读懂失败日志并定位根因。 把报错与复现步骤回灌,让它给出最小改动修复,而不是重写整个模块。

操作步骤

Step 1|生成设计规格与验收契约。 用一句话需求换取结构化规格,人工确认后再进入编码。

Step 2|生成代码骨架。 明确引擎版本、目录结构、命名规范,要求逐场景实现,避免一次性生成整个游戏。

Step 3|生成无头冒烟测试。 关键动作:让测试 模拟真实操作 (注入输入或直接驱动角色),而不是只调用函数。

Step 4|本地/CI 跑测。 命令示例:

  1. godot --headless --path . -s res://tests/smoke_test.gd

Step 5|失败日志回灌修复。 把完整输出(含失败断言与堆栈)贴回模型,要求“最小改动 + 说明为什么之前会漏”。

Step 6|回归。 修复后重跑全部断言,防止修 A 坏 B。

可直接复制的 Prompt

Prompt 1:从需求到验收契约

  1. 你是资深游戏程序。请把下面的需求转成机器可读的验收契约,不要写实现代码。
  2. 【游戏需求】2D平台跳跃,3条生命,尖刺扣1血并有1秒无敌,集齐5枚金币后触碰终点通关,掉出下边界重生到最近检查点。
  3. 【引擎】Godot 4.3,GDScript。
  4. 【输出要求】
  5. 1. 输出 JSON,字段包含 game_id / engine / genre / win_condition / fail_conditions / entities / acceptance_checks;
  6. 2. acceptance_checks 每条必须可在无头模式下自动判定,写明前置状态、操作动作、期望状态;
  7. 3. 至少覆盖:碰撞伤害、无敌帧、胜负判定、重生逻辑、边界情况;
  8. 4. 不要输出任何解释性文字,只输出 JSON。

Prompt 2:生成可运行的无头测试

  1. 基于以下验收契约,编写 Godot 4.3 无头测试脚本 tests/smoke_test.gd。
  2. 【契约】{粘贴 playability_contract.json}
  3. 【要求】
  4. 1. extends SceneTree,通过 godot --headless -s 运行,全部通过打印 SMOKE TEST PASS 并以 0 退出,失败以 1 退出;
  5. 2. 每条断言对应一个 AC 编号,失败时打印 AC 编号、期望与实际值;
  6. 3. 必须驱动真实运行:实例化关卡场景、await physics_frame 让物理生效、通过设置角色位置或 Input.action_press 触发交互;
  7. 4. 禁止用“检查变量是否被赋值”代替行为验证;
  8. 5. 给出运行命令与依赖说明。

Prompt 3:失败日志定位与修复

  1. 以下是测试输出,请定位根因并修复。
  2. 【失败输出】{粘贴完整日志}
  3. 【相关代码】{粘贴 player.gd / spike.gd / level_01.gd}
  4. 【要求】
  5. 1. 先说明失败属于哪类问题(碰撞层配置 / 信号未连接 / 状态机缺分支 / 判定条件恒真);
  6. 2. 给出最小改动 diff,不重写无关模块;
  7. 3. 说明这次修复为什么不会破坏已有通过的 AC;
  8. 4. 如怀疑是场景文件(.tscn)配置问题,指出需要检查的具体节点与属性。

测试脚本骨架示例

  1. extends SceneTree
  2. const LEVEL := preload("res://levels/level_01.tscn")
  3. var failures: Array[String] = []
  4. func _initialize() -> void:
  5. _run.call_deferred()
  6. func _run() -> void:
  7. var level: Node = LEVEL.instantiate()
  8. root.add_child(level)
  9. await process_frame
  10. await physics_frame
  11. var player = level.get_node("Player")
  12. var spike := level.get_node("Spikes/Spike01")
  13. # AC-01:真实驱动角色撞上尖刺,验证扣血而非仅检查变量
  14. var hp_before: int = player.health
  15. player.global_position = spike.global_position
  16. await physics_frame
  17. await physics_frame
  18. _check("AC-01", player.health == hp_before - 1,
  19. "health %d -> %d" % [hp_before, player.health])
  20. # AC-02:未集齐金币时触碰终点不应通关
  21. level.coins_collected = 2
  22. level.get_node("Goal").emit_signal("body_entered", player)
  23. await process_frame
  24. _check("AC-02", not level.is_won(), "is_won should be false")
  25. # AC-03:集齐金币后触碰终点应通关
  26. level.coins_collected = level.total_coins
  27. level.get_node("Goal").emit_signal("body_entered", player)
  28. await process_frame
  29. _check("AC-03", level.is_won(), "is_won should be true")
  30. if failures.is_empty():
  31. print("SMOKE TEST PASS")
  32. quit(0)
  33. else:
  34. for f in failures:
  35. printerr("FAIL " + f)
  36. printerr("SMOKE TEST FAILED (%d)" % failures.size())
  37. quit(1)
  38. func _check(id: String, ok: bool, detail: String) -> void:
  39. if ok:
  40. print("PASS " + id)
  41. else:
  42. failures.append("%s: %s" % [id, detail])

这段脚本的关键设计意图,正是 SWE-Game 强调的那一点: 先构造前置状态,再执行真实交互,最后断言运行时状态 ,而不是断言“某个变量看起来对”。

五、常见坑与排查建议

症状 优先检查 处理建议
无头模式行为与编辑器不一致 是否依赖渲染帧或视口尺寸 用 physics_frame 而非 process_frame 等待物理生效
测试通过但游戏仍不对 断言是否绕过了真实交互 改为注入输入或真实位移,禁止直接改内部变量后断言
AI反复“修好又坏” 是否缺少回归集合 把全部 AC 固化进 CI,每次提交全量跑
3D 场景判定漂移 碰撞体尺寸与角色参数不匹配 把角色参数与关卡几何一起交给模型做联合校验
生成代码风格不一致 缺少项目约定 在 Prompt 中固定目录结构、命名规范、引擎版本

六、进阶:把这条回路交给智能体

当游戏规模变大,人工搬运日志会成为瓶颈。可以把整个循环交给 Agent 工作流:读取契约 → 生成/修改代码 → 调用本地 Godot 无头执行 → 解析输出 → 决定修复或结束 → 提交回归报告。ERNIE 5.5 Flash Preview 在编程、工具调用与多步骤任务上的优化,正适合承担这类“规划 + 调用 + 判断”的角色;执行环节仍需由本地环境或 CI 完成。

落地时建议保留三条护栏: 契约先行 (不允许无验收标准就生成)、 证据优先 (只接受运行时断言结果作为通过依据)、 最小改动 (禁止用重写掩盖根因)。

七、关键步骤回顾

  1. 承认瓶颈:AI做游戏的失败大多发生在运行时语义层,不是语法层。
  2. 参考 SWE-Game 的思路:为每款游戏准备明确的设计意图 + 可运行的参照实现。
  3. 把设计意图写成 playability_contract.json,让验收标准可执行。
  4. 用无头模式跑真实交互测试,断言运行时状态而非变量赋值。
  5. 失败日志回灌模型定位根因,全量回归防止反复。
  6. 规模上来后交给 Agent 工作流,并保留契约、证据、最小改动三条护栏。

想验证这套方法,可以先在 ERNIE 5.5 Flash Preview 在线体验 里粘贴你的游戏需求和验收契约,看看它生成的测试用例是否真的在“驱动角色玩”,而不是在“检查变量”。开发者也可以前往 百度智能云千帆模型详情页 了解模型能力、上下文规格与接入方式,再把测试回路接到自己的 Godot 项目与 CI 上。

发表评论

活动