后记:Idle Frontier 与用 AI 开发游戏
论文信息
- 原文:查看原文
你能从标注数据起步,在 1000 天内一步步做到训练出前沿模型(frontier model,指当下性能顶尖的 AI 模型)吗?来玩一玩《Idle Frontier》吧!在 itch.io 上体验《Idle Frontier》
在这篇博客里,我想分享我借助 Opus 5(下文中我就直接叫它 Claude)开发这款游戏的经历。我想给出的是一种冷静看待 AI 辅助游戏开发的视角:既不像最近流行的“零样本生成 three.js 游戏”那样被吹得神乎其神,也不像“我的工作流里绝不用 AI”的那群人那样彻底排斥。
先说明一下,我只是个业余游戏开发者。我学过像素画和 Godot(一款开源游戏引擎),之前做过四个游戏,但都不算精致。不过,还是先说说这个游戏是怎么来的吧?
想法
我博士第一学期的一个项目,是写一篇关于语言模型在数据和算力约束下如何演进的文献综述。语言模型对这些资源的需求非常大;Kaplan 等人(2020)和 Hoffmann 等人(2022)已经将这一类规律总结成了“缩放定律”(scaling laws,即模型性能随数据量、算力、参数规模呈幂律增长的规律)。但关键在于,在那些资源稀缺的环境里——比如“全球南方”(Global South,泛指发展中国家和地区)——你该怎么构建语言模型呢?
我觉得这类问题有一种共同的形态,可以被模拟成一个“资源引擎”问题。最开始,我想做一个类似二十一点的卡牌游戏,但后来我遇到了《Inn Over Your Head》这款游戏,突然意识到:在数据和算力之间做权衡,正好可以做成一个完美的挂机增量游戏(idle incremental game,通过简单重复操作积累资源,再逐步解锁自动化玩法)!我认为“点击”这个动作用来代表“标注数据”非常贴切:因为标注数据是少数几个你真正能自己掌控的抓手。没错,如果你想标注一个巨大的语料库,确实会耗费大量时间和金钱,但只要你真的想做,这件事在现实中是可行的。于是,我又引入“研究”机制来减轻标注的负担(比如用合成数据给你变出一些数据来)。到这个时候,我就意识到:这里面真的能做出一个游戏。
我承认,最终发布的《Idle Frontier》和我最初构想的样子差了不少。这款游戏并没有太关注“全球南方”(Global South,指发展中国家)的现实处境,因为最终的落脚点是做一个前沿级别的语言模型。我一度想通过限制算力获取来把它拉回正轨——除了拿资助,没有别的途径赚算力——但这样一来,游戏玩法被束缚得太紧,乐趣全无。最后只有“主权AI资助机制”(Sovereign AI grant)保留了下来:它会给你一点点算力,用来完成国家目标。这个点子我以后可能还会再捡起来,但作为第一次尝试,我对《Idle Frontier》现在的样子已经挺满意了。
用AI开发游戏
这节我想聊聊我使用Claude开发《Idle Frontier》的体会,主要放在三个方面:编程、美术和游戏玩法。
编程
最近,语言模型在写代码这件事上确实很拿手,我觉得这也是Claude帮了我最多的部分。我上一次做游戏是2022年,用的还是Godot 3;四年过去,Godot已经更新了好几个大版本。另外,我也不太好意思提自己的GDScript代码习惯:我做过的游戏项目,大多是意大利面式代码(spaghetti code,指结构混乱、东拼西凑的代码),这里补一块、那里修一下。
和Claude合作给我带来两个好处:一是把我早就生疏的Godot 3知识搬迁到新版本,二是学到了真正的游戏设计模式。没错,光是读Claude生成的代码,我就学了几招!拿信号(signal)来说,我原来在Godot 3里要用yield(...)去等待信号;而Godot 4里信号变成了对象,改用await来调用。此外,在把多个信号一起管理时,我还学会了事件总线模式(event bus pattern):
signal data_changed ( total : int )
signal mission_claimed ( mission : Mission )
signal grant_claimed ( grant : GpuGrant )
func add_data ( amount : int ) -> void:
data += amount
EventBus.data_changed.emit( data )
func _ready () -> void:
EventBus.data_changed.connect( _refresh )
这样一来,我不用再惦记自己建过的每一个信号,只要让EventBus当所有变化的“中转站”就行——太棒了!
后记:Idle Frontier 与用 AI 开发游戏
我开发 Idle Frontier 的工作流程是:先在 Godot 编辑器里搭建场景,再写出我想要的交互函数。一开始,我只请 Claude 帮我修 bug、做调试,但后来发现它在操作 Godot 方面的能力相当惊艳——而且完全没装任何插件!实际上,有几个界面(Screen)就是 Claude 从头到尾独立完成的。
虽说我确实可以想象,让 Claude 在几乎不需要人工参与的情况下做出一个 Godot 游戏(就像那些零样本的 three.js 小游戏一样),但 Claude 仍然会漏掉很多细节。比如,很多时候实现一个新功能会把另一个功能搞坏;又比如界面元素的间距总是不对劲。我觉得一个有用的做法是:让自己心里始终装着整个代码库的“大形状”或上下文,然后让 Claude 去实现细节。难点通常在于,如果你“凭感觉编程”(vibe-code,指不深究底层逻辑、让 AI 代写的开发方式)太多,就会逐渐失去对代码库结构的把握,最后只能陷入一遍又一遍打地鼠式的补丁循环,非常沮丧。
尽管有这些局限,Claude 还是帮我更快地重新熟悉了 Godot。说实话,过去几年我没有回到游戏开发,原因之一就是重新学一套框架实在太劝退。但 Claude 让我轻松地重新回到了 Godot 的怀抱!
美术与素材
Claude 借助工具调用能力,已经可以操作 Aseprite(一款像素画编辑软件)来画像素画了。但根据我的经验,它还远没到能直接交付的程度。比如,让它画一只羊驼并做动画就非常费劲,所以最后还是我自己动手。我试过很多办法去“引导”Claude,比如给它参考图,或者用非常啰嗦的文字描述。
在这个过程中,我认识到一件事:我想追求的是一种很具体的感觉,但这种感觉很难用语言描述,属于“我看到了才知道是不是那个味儿”。也许正因为如此,我宁愿直接在 Aseprite 里反复迭代,而不是在聊天框里来回折腾。
后记:闲置边疆与用 AI 开发游戏
尽管有这些限制,Claude 在绘制非常简单的 UI 图标方面已经相当出色:这些用户界面图标就是 Claude 用 Aseprite(一款像素画编辑器)生成的。这个功能很实用,因为我只需要描述自己需要什么(比如“能画一个芯片吗?”),它就能立刻导出。Claude 在处理与工作流相关的任务时也表现得不错,比如创建画布、导出 PNG/GIF 图片,或者生成精灵表(把多帧动画拼成一张大图的素材表)。这真的很酷;我甚至可以直接说“做一个灯泡,再配一段它点亮过程的 8 帧循环动画”。再次强调,这类节省时间的基础操作,Claude 都完成得相当不错。