One-shotting a Raccoon Heist game using Claude Fable 5

Simon Willison

用 Claude Fable 5 一次性做出《浣熊劫案》游戏

原文由 Simon Willison 发布,订阅该博客

早在 2022 年我曾发过一条推文,分享了由 GPT-3 生成的游戏概念截图,以及用 DALL-E 创作的概念“美术图”。今天,正值那条推文发布四周年,我决定试试 Claude Fable 5(运行在Claude Code for web 上)能否仅凭那条推文的内容就把整个游戏做出来。结果它完成得相当不错!

你可以在这里试玩游戏。这是GitHub 仓库,还有一段简短的演示视频:

我是如何构建的

这是2022 年 8 月 5 日的那条推文

Simon Willison @simonw - 新爱好:用 GPT-3 和 DALL-E 的组合在 60 秒内为电子游戏做原型。这里是“Raccoon Heist”。两张图片——第一张是 GPT-3 playground 的截图,下方是提示词。右边是一张明显由 DALL-E 生成的、近似等轴视角的 3D 电脑游戏画面,两只浣熊正沿着街道悄悄潜行,经过一栋颇具未来感的建筑,看起来正打算偷点什么。

我当时给 GPT-3 的提示词是:

Write a detailed product description of a computer game where a team of raccoons go on heists.

那还是文本补全的时代,你让 GPT-3 去补全接下来的文本。它补全的内容是:

在《Raccoon Heist》中,你和你的浣熊盗窃小队将执行一系列大胆的劫案。从抢银行到窃取名画,再大再小的活儿都难不倒你们这群毛茸茸的家伙。你需要运用智慧和技巧躲避警察,带着战利品全身而退。凭借令人兴奋的玩法和讨人喜欢的角色阵容,《Raccoon Heist》是任何想体验轻松劫案游戏的玩家的完美之选。

我给 DALL-E 的提示词很短:

Screenshot from a video game where a team of raccoons go on a heist

今天的实验是:我能否把这些截图直接丢给 Fable 5,让它根据提示去写一个游戏,然后完全放手让它自己完成,最终得到一个可运行的游戏?

让 Claude Code for web 支持 GitHub Pages

Claude Code for web 一个让人头疼的地方是,在它仍在工作时,很难测试它正在做的内容。

我一直用 GitHub Pages 来绕过这个限制,效果非常好。

我的流程如下:

  1. https://github.com/new 为项目创建一个新仓库——公开或私有都可以,这个方法对两者同样有效。
  2. 在 Claude iPhone 或桌面应用中,或在浏览器中打开 https://claude.ai/code,启动一个 Claude Code for web 会话
  3. 告诉 Claude 要做什么,并鼓励它尽快提交一个 index.html 页面。这会创建一个类似 claude/3d-raccoon-heist-game-50n293 的分支
  4. 进入该仓库的 Settings -> Pages 区域(在我的例子中是 github.com/simonw/raccoon-heist/settings/pages),选择“Deploy from a branch”,选中分支名称,然后点击 Save。

就这么简单!每次推送后大约 30 秒内,最新内容就会出现在 yourname.github.io/your-repo/ 上。

如果你用的是私有仓库,任何能猜到仓库名称的人都能查看已发布的内容。我自己倒不太担心这一点。

Fable 5 的提示词

以下是我给 Fable 5 的提示词(在手机的备忘录应用里写的——整个项目都是在手机上完成的)。我还附上了原推文中的两张图片。

Build this 3D game, for the browser.

This repo is configured to serve static files so make sure there is an index.html that loads everything else.

Make sure it is mobile-friendly (touch controls, works well on small screens).

You have an OpenAI API key and access to their image generation model APIs, use that for textures to use with your 3D models. Docs here: https://developers.openai.com/api/docs/guides/image-generation - use gpt-image-2

Work independently - do not ask me to make any further design decisions. Make sure the game is fun, a little surprising, has good raccoon heist vibes, and is visually pleasing.

Commit and push as often as possible so I can preview your work - start with an index.html that presents a title screen, then build from there.

Append to a notes.md file as you work, including your changes to that as part of every commit.

我没有指定任何技术选型。我(正确地)假设,基于之前的实验,它大概率会使用Three.js

给 Claude 一个 OpenAI 密钥来弥补其能力短板,事实证明效果很好——在这个例子中,我们需要一种生成图像来用作纹理的方法。而 Fable 非常擅长为图像生成模型写提示词!

我说“独立工作——不要让我再做任何设计决策”,是因为我想看看它能否在没有任何进一步输入的情况下,做出一个完整、可运行的游戏。

我还说了“尽可能频繁地提交和推送,以便我能预览你的工作”。当你在 Claude iPhone 应用中使用 Claude Code 时,你会给它一个 GitHub 仓库,它会在一个分支上工作。让它“尽可能频繁地推送”意味着提交会立刻出现在那个分支上。

我喜欢让它写 notes.md 来增加一点趣味性——这是完成后的文件,以及它添加那只狗时写下的记录:

新增挑战:从第 3 夜起,院子里会出现一只巡逻的看门狗——一只戴着带刺红色项圈、尾巴摇摆的低多边形棕色猎犬。它会在随机地点之间游荡,一旦进入 12 个单位范围内,就会通过气味追踪你(与视线无关——全靠鼻子,头顶会显示 👃 并伴有吠叫)。如果你拉开 17 个单位的距离,它就会放弃追踪。被抓时的提示现在会区分来源:看守/车灯/猎犬。已通过自动化测试验证了游荡 → 追踪 → 被抓的流程。

回顾执行记录

你可以查看Claude Code 的共享会话,我还用自己的 claude-code-transcripts 工具导出了 HTML 版本,你可以在这里查看

Fable 首先创建了一个索引页面,引入了一份 Three.js 的本地副本,然后自己写了一个gen_textures.py 脚本备份在此)。

它生成了纹理并抽检确认外观没问题。它为垃圾桶生成的 metal.jpg 文件看起来是这样的,不过我觉得在游戏里并没有完全正确地贴上去:

一张深蓝灰色铆钉金属板的游戏纹理图集,左上角有一个带把手的圆形舱口,中间是带棱的波纹板,左下角是一块素色的圆形板,顶部和底部是扁平的带状条纹。没有可见文字。

然后它构建了游戏的第一个基础版本,接着决定用 Playwright 在预装的 Chromium 中进行“冒烟测试”。这意味着它可以给自己的成果截图并亲自检查。它分别对桌面和移动端宽度进行了测试,随后发现在移动端宽度下浣熊不可见,于是修复了这个问题

浣熊、垃圾桶藏身处以及两只同伴浣熊现在在移动端已完全可见。正在提交这一关键修复。

它决定生成一个标题画面,确实也这么做了,使用的是这个 gen_title.py 脚本。以下是它为此使用的 gpt-image-2 提示词:

Video game key art, low-poly 3D render style, moody nighttime scene: a cute low-poly raccoon wearing a tiny black burglar mask sneaking on its hind legs carrying a glowing gold coin, next to a tipped-over metal trash can, suburban house with warm glowing windows in the background, deep blue night, full moon, fireflies, cinematic rim lighting, charming heist caper mood. No text, no words, no logos.

这是生成的图像(Claude 觉得它“非常漂亮”)——不过我注意到,在桌面端显示时,它只裁剪显示了顶部的三分之一,浣熊反而看不到了!

一只多边形浣熊在被推翻的垃圾桶旁抱着一枚金币,背景是房屋和月亮。

然后是我最喜欢的一处改动:它加入了那只狗

export function makeDog() {
  const g = new THREE.Group();
  const BROWN = 0x8a6440, DARK = 0x5e4128;
  const body = new THREE.Mesh(new THREE.SphereGeometry(0.42, 10, 8), M(BROWN));
  body.scale.set(0.9, 0.8, 1.5);
  body.position.y = 0.55;
  body.castShadow = true;
  g.add(body);
  const head = new THREE.Mesh(new THREE.SphereGeometry(0.3, 10, 8), M(BROWN));
  head.position.set(0, 0.85, 0.62);
  g.add(head);
  const snout = new THREE.Mesh(new THREE.SphereGeometry(0.16, 8, 6), M(DARK));
  snout.scale.set(0.9, 0.7, 1.3);
  snout.position.set(0, 0.76, 0.9);
  g.add(snout);
  const nose = new THREE.Mesh(new THREE.SphereGeometry(0.06, 6, 6), M(BLACK));
  nose.position.set(0, 0.78, 1.08);
  g.add(nose);
  for (const s of [-1, 1]) {
    const ear = new THREE.Mesh(new THREE.SphereGeometry(0.12, 6, 6), M(DARK));
    ear.scale.set(0.7, 1.3, 0.5);
    ear.position.set(0.2 * s, 1.08, 0.55);
    g.add(ear);
    const eye = new THREE.Mesh(new THREE.SphereGeometry(0.05, 6, 6), M(0x1a1a1a, { emissive: 0x331111 }));
    eye.position.set(0.13 * s, 0.92, 0.86);
    g.add(eye);
  }
  const tail = new THREE.Mesh(new THREE.CylinderGeometry(0.05, 0.09, 0.5, 6), M(DARK));
  tail.position.set(0, 0.8, -0.62);
  tail.rotation.x = 0.8;
  g.add(tail);
  // spiked collar
  const collar = new THREE.Mesh(new THREE.TorusGeometry(0.22, 0.05, 6, 12), M(0xc0392b));
  collar.position.set(0, 0.78, 0.5);
  collar.rotation.x = Math.PI / 2.4;
  g.add(collar);
  const legGeo = new THREE.CylinderGeometry(0.07, 0.09, 0.34, 6);
  const legs = [];
  for (const [x, z] of [[-0.22, 0.35], [0.22, 0.35], [-0.22, -0.35], [0.22, -0.35]]) {
    const leg = new THREE.Mesh(legGeo, M(DARK));
    leg.position.set(x, 0.17, z);
    g.add(leg);
    legs.push(leg);
  }
  let phase = Math.random() * 10;
  return {
    group: g,
    animate(dt, speed) {
      phase += dt * (3 + speed * 10);
      const amp = Math.min(0.6, 0.1 + speed * 0.6);
      legs[0].rotation.x = Math.sin(phase) * amp;
      legs[3].rotation.x = Math.sin(phase) * amp;
      legs[1].rotation.x = -Math.sin(phase) * amp;
      legs[2].rotation.x = -Math.sin(phase) * amp;
      tail.rotation.z = Math.sin(phase * 1.5) * 0.4;
      body.position.y = 0.55 + Math.abs(Math.sin(phase)) * 0.04 * (0.3 + speed);
    },
  };
}

并用 Playwright 对其进行了一轮测试,包括又一张截图

  // walk near the dog
  await page.evaluate(() => { const d = window.__rh.dog; window.__rh.teleport(d.x + 6, d.z); });
  await page.waitForTimeout(2000);
  info = await page.evaluate(() => JSON.stringify({ dog: window.__rh.dog, state: window.__rh.state, player: window.__rh.debug().player }));
  console.log('after approach:', info);
  await page.waitForTimeout(3000);
  info = await page.evaluate(() => JSON.stringify({ dog: window.__rh.dog, state: window.__rh.state }));
  console.log('after chase:', info);
  await page.screenshot({ path: __dirname + '/shot-dog.png' });

游戏截图。第 3 夜,浣熊在画面中央与狗重叠。一条消息显示:被抓了!看守把你赶走了!屏幕边缘泛起红色。

它又做了一些微调,然后宣布大功告成

Raccoon Heist 已构建、测试并推送完成——在 claude/3d-raccoon-heist-game-50n293 分支上共 7 次提交,全部通过了桌面端、竖屏手机和横屏手机视口的自动化 Playwright 试玩验证。

游戏内容:你扮演一只戴面具的浣熊,从同伴们的垃圾桶藏身处悄悄溜进月光下的死胡同。收集硬币、宝石、戒指、甜甜圈和鱼(它们会 visibly 堆在你背上并让你行动变慢),然后在天亮前摇摇晃晃地回家存起来。每一夜目标都会提高,难度也会加大:第 1 夜有一个用手电筒巡逻的看守,第 2 夜有两个,第 3 夜还会加入一只完全无视视线的气味追踪猎犬。还有一辆警车会用车头灯扫过街道,住户会随机从窗户向外张望,一只海鸥会定期俯冲抢走你尚未捡起的战利品,披萨片会触发 6 秒的极速 FRENZY 状态,而一个罕见的金色电视机如果你能把它扛回家,就价值 120 分。获胜等级:TRASH PANDA ⭐ → CAT BURGLAR ⭐⭐ → MASTER OF DISGUISE ⭐⭐⭐,你的最佳成绩会保存在 localStorage 中。

构建方式:本地引入的 Three.js(无 CDN)、程序化生成的低多边形角色,以及程序化生成的 WebAudio 原声(潜行感的 walking-bass 爵士乐加上所有音效——零音频文件)。所有七张地面/墙面纹理以及标题画面的主视觉图都是用 gpt-image-2 生成并作为静态资源提交的,因此部署后的游戏不会发起任何 API 调用。移动端会获得一个动态触控摇杆(在你触摸的任意位置出现)外加一个 DASH 按钮;桌面端则使用 WASD/方向键和空格键。

测试中发现了两个值得一提的真实 bug:画布在任何真机上都会以 2 倍尺寸渲染(一次 cssText 赋值清掉了 Three.js 的内联尺寸设置——而桌面端 DPR-1 的测试完全掩盖了这个问题),以及胜利画面的星级评分 div 继承了标题画面的全屏 .stars CSS,导致“进入下一夜”按钮的所有点击都被悄悄吞掉。两者都已修复并加入了回归测试,同时还覆盖了拾取、存入、追逐/被抓、天亮、关卡推进以及失败/重试等流程。

notes.md 中有完整的构建日志。游戏已在分支上上线——只需托管仓库根目录并打开 index.html 即可。

游戏好玩吗?

作为一个起点,它令人印象深刻,但还算不上是一款游戏。

你操控浣熊在院子里四处移动,收集物品——甜甜圈、鱼、金币、珠宝——同时躲避拿着手电筒的看守,以及在后面关卡中出现的狗。你的负重有限,背包满了就得把东西运回垃圾桶。如果捡到披萨片,你会获得短暂的加速效果。

完全没有任何团队协作机制——垃圾桶旁边还有另外两只静止的浣熊,但它们纯粹是装饰。

随着关卡推进,游戏会稍微变难一些——第 3 关加入的狗是最有意思的新机制——但通关还是非常、非常容易。而且也相当无聊——每一夜都有固定的时长,即使你已经把所有物品都收集完了,也只能无所事事地等到天亮。

它的实现让我印象深刻。它是全 3D 的,有垃圾桶,手电筒的光锥效果很有趣,整体视觉风格也相当统一。它能在手机上运行。音乐(按 Claude 的说法是“程序化生成的 WebAudio 原声(潜行感的 walking-bass 爵士乐加上所有音效——零音频文件)”)很简单,但感觉恰到好处。

作为一款完整的游戏项目,它只能算平庸。但作为单条提示词的起点,我认为它已经非常令人印象深刻了。

到目前为止,我已经用 vibe coding 做了不少游戏。从玩法角度来看,它们都让人相当失望——事实证明,设计出有趣的游戏仍然是人类独有的能力,而且这需要的技巧和经验远超 Claude 或我所能提供的。

话虽如此,我还是非常推荐把游戏开发项目当作探索智能体能力的一种方式。这是一种有趣、低风险的尝试新事物的方法。如果你坚持做得足够久,说不定真能做出值得一玩的东西!

2026 年 8 月 7 日更新:我用运行 GPT-5.6 Sol Ultra 的 OpenAI Codex Desktop 尝试了同样的提示词,得到了明显更好的结果——GPT-5.6 Sol 领会到了浣熊小队去执行劫案这一设定的重要性,并构建了一个需要你先在博物馆中解救两名同伴,然后叠在它们身上去偷走金色沙丁鱼的游戏。

本文章由 muse-spark-1.2-contributor 进行翻译

评论