一句话生成农场游戏的工程拆解
Spawn Point 用微调模型输出游戏设计 JSON,再由 Python 和经过测试的 Godot 4 组件生成可玩游戏。作者展示基于地形掩码、噪声与固定随机种子的程序化美术方案,并复盘实现中的问题。
完整中文译文
一句话生成能玩的农场,难点并不止于让模型听懂「种南瓜、养鸡、提防乌鸦」。Spawn Point 作者把生成范围收窄到休闲农场后,真正花力气处理的是地形边缘、树木碰撞、存档污染,以及从射击游戏继承来的错误行为。
这套方案用小型微调模型输出 JSON 游戏设计,再由 Python 和经过测试的 Godot 4 组件完成构建。它值得看的地方,是把哪些工作交给模型、哪些规则留在代码里,以及这些边界如何被一次次故障修正。
农场看起来舒服,靠的是可控的绘制规则
作者没有采用最初设想的瓦片集加自动拼接规则,而是把整张地图的地面绘制成一张图片。地图仍然用字符网格描述:. 表示草地,: 表示道路,~ 表示水。
每种地形先变成像素遮罩,也就是标出哪些区域属于它,再放大 16 倍、模糊,最后加入少量噪声做阈值处理。模糊让边缘圆润,噪声让轮廓出现细小起伏,减少规则网格的机械感。
其他细节也由明确规则生成:贴着遮罩外侧的像素画成深色轮廓;陆地下方 6 px 内的水像素画成棕色悬崖;草丛、花、睡莲和芦苇按格子撒入。随机数带固定种子,因此同一座农场能保持相同外观。

替换颜色表和树木列表,同一套代码就能画出春天、秋天、冬天和沙漠。作者强调,画面中的地块、树木、鸡和卷心菜都由 Python 在构建时绘制,无需预先准备这些美术图片。
这条路线也暴露了工程问题。模糊和距离变换最初依赖 scipy.ndimage,本机正常,CI 却失败,因为 scipy 没有被声明为依赖。作者改用 numpy 实现可分离模糊和腐蚀,又因图片外部被当成陆地,生成了一圈浅色边框,最后通过 mode="edge" 的填充方式修正。
程序化绘制并没有消除复杂度。它把复杂度转移到了可以复现、调试的图形规则上。
从能走动到能生活,需要补齐哪些系统
俯视角农场要有空间感,角色就得能走到树后面。作者为此补了两个能力:按屏幕纵坐标排序绘制,以及只在物体脚部设置碰撞区域。
前者让画面位置更低的实体显示在前面;后者避免整棵树的精灵图都参与碰撞,导致树冠挡住道路。它们最终成为游戏图中的配置项:"y_sort": true 和 "base": [22, 14]。
接下来,农场从单个关卡变成持续几天的季节。每天从 6:00 到 22:00,傍晚有金色光照,夜晚变暗;玩家睡觉后,浇过水的作物才会生长。雨天自动保持作物湿润,冬季则下雪。种子要花钱,动物喂食后才产蛋或产奶,工作台出售工具升级。
这些系统把移动和交互串成了经营循环,也引出了状态管理问题。
存档写入 user://,浏览器中对应 IndexedDB,保存日期、金钱、每块田地、动物和被砍掉的树。GIF 录制器和自动试玩机器人必须避开正式存档,否则下次录制就会接着上次机器人的进度开始。按 R 重启也必须清空存档,否则只是重新加载同一天。
作者还发现,演示里的农夫会倒着走。原因不是精灵翻转失败,而是录制自动驾驶沿用了射击游戏的瞄准逻辑,总朝向最近的敌人。农场里的乌鸦成了目标,角色离开时仍盯着它。真实游玩中,持剑角色跟随鼠标瞄准也会触发类似问题。修复只有两行:农夫始终朝移动方向看。
组件经过测试,也仍然可能带着不适合当前玩法的默认假设。
模型漏掉狐狸,为什么不能只补狐狸样本
模型能理解「有乌鸦吃庄稼的农场」,却容易漏掉随口提及的动物,例如越南语 có cáo,即「有狐狸」,以及「附近有浣熊」。作者在训练前封存了 16 条此类提示,已发布模型答对了 12 条。
第一次补训练修好了狐狸,却引入两个退化:和平农场开始出现害兽,用户指定的农夫身份也被忽略,例如猫农夫变成默认的 Farmer。
原因出在合成数据。新增样本全部使用默认农夫,而且几乎都有害兽。模型学到的不只是「狐狸需要被识别」,还包括这些样本里反复共现的默认身份和害兽设定。
第二次补丁让一半样本使用有名字或指定身份的农夫,增加明确要求「没有害兽」「和平」的农场,并回放更多前一轮数据。作者称 v5.2 在两个开发集上落后一两条提示,属于其运行间波动范围。不过,输入原文没有展示完整结果表,无法据此比较各版本的整体提升。
发布后,越南语 nuôi gà và câu cá,即「养鸡和钓鱼」,还曾让 Fish 进入害兽列表。作者随后在规范化处理器中,把农场动物和鱼从害兽名单里移除。
当输出违反明确的业务分类时,代码校验往往比继续追加训练更直接。
原文提供的本地尝试入口如下,分别用于安装、启动模型服务和生成农场:
pip install -U spawnpoint-studio
spawnpoint llm up # serves the model (12 GB NVIDIA GPU + Docker, or Ollama)
spawnpoint create "a cozy autumn farm with pumpkins, watch out for crows"
要留意第二行:作者给出的服务条件是 12 GB NVIDIA GPU 加 Docker,或使用 Ollama;原文没有进一步列出两条路径各自的完整配置。
我的判断
我认可这套分工:模型负责把自然语言整理成结构化设计,地形、碰撞、存档和分类约束由确定性代码接住。相比继续扩大模型的生成职责,它更容易定位错误,也更容易保留已经修好的行为。
但这份成果有明显前提:作者主动收窄到了休闲农场,并投入了专用组件和绘制规则。它展示的是一个垂直类型如何逐步做好,不能直接证明同样的方法能低成本覆盖所有游戏类型。画面质量和迭代效率也没有对照数据,目前属于作者的实现经验。
手机测试同样要看边界。作者使用手机尺寸的 headless Chrome,最初手动派发合成 PointerEvents,没有触发控制,因为 setPointerCapture() 遇到了浏览器未登记的 pointer id。改用 DevTools 协议输入后,摇杆能把角色带到门口,ACT 也能进入房屋;测试还发现种子按钮遮住屏幕中央,随后改为点击快捷栏选择种子。
这验证了部分触控交互,却不能替代真实手机上的性能、浏览器差异和手感测试。原文没有给出这些结果。
能拿来做什么
- 做自然语言生成配置的前端或 Node 工具时,可以参考「模型输出 JSON,代码负责执行与校验」的分工,把明确的分类限制放进规范化处理,减少错误配置进入运行阶段的机会。
- 给 AI 编程助手补示例或维护微调数据时,要同时保留目标场景、反例和旧能力用例。修复狐狸识别后,还应检查和平农场和指定角色有没有退化。
- 给带存档的浏览器应用做演示录制或自动试玩时,应隔离测试状态与正式状态,并检查重启按钮是否真的清除了持久化数据。
- 测试依赖指针捕获的触控组件时,优先使用浏览器认可的输入路径。合成事件无效时,先确认输入机制,再判断应用是否存在交互故障。