1. 一场“同题异构”的AI能力实测
第一次看到“12家大模型做同一个游戏”这个玩法,我脑子里蹦出来的不是“谁最强”,而是“这评测方式终于有人干了”。过去我们评测大模型,翻来覆去就是写诗、算数学、答选择题,顶多让它写个贪吃蛇。但贪吃蛇这种项目,代码量太小,根本拉不开差距,随便一个能写循环的模型都能糊弄过去。真正能暴露问题的,是那种有状态、有交互、有边界条件、还得跑起来不崩的完整小游戏。
这个项目做的事情,就是给12个不同的大模型扔了同一道题:用虚幻引擎做一个能跑能玩的小游戏。然后看谁做出来的东西最像样,谁翻车最惨。最讽刺的是,收费最贵的那家,交出来的东西反而最拉胯。这事儿在圈子里传开之后,很多人第一反应是“不可能吧”,第二反应是“那到底该怎么选模型”。
我先说结论:模型的价格和它在具体工程任务上的表现,相关性远比我们想象的要弱。贵,可能只是因为它参数多、训练成本高,或者单纯就是品牌溢价。但做游戏这种活儿,考验的是模型对引擎API的理解、对代码结构的组织、对运行时错误的预判,这些能力跟“贵不贵”没有必然联系。
这篇文章我会把这个天梯评测的底层逻辑拆开,讲清楚为什么用虚幻引擎做同题异构能测出真东西,12个模型大致分成了哪几个梯队,最贵的那个到底翻在了哪里,以及如果你自己也想搞类似的评测,该怎么设计流程、怎么避坑。不管你是做AI应用选型的,还是单纯好奇大模型写游戏代码到底靠不靠谱,这篇应该都能给你一些能直接抄的参考。
2. 为什么偏偏选虚幻引擎来做这个局
2.1 虚幻引擎的“工程复杂度”是天然的筛选器
很多人可能觉得,做个游戏而已,用Unity或者Godot不是更轻量吗?为什么非要上虚幻?这个问题我一开始也想过,但仔细琢磨之后发现,虚幻引擎的复杂度恰恰是这个评测最有价值的地方。
虚幻引擎的项目结构本身就比轻量引擎重得多。一个最基础的C++项目,你至少得处理这几个层面:GameMode定义游戏规则、Pawn或Character定义玩家实体、PlayerController处理输入、HUD或UMG做界面、GameInstance管理全局状态。这还没算上资产引用、模块依赖、编译配置这些琐碎但致命的东西。一个模型如果只是“会写代码”,但不懂虚幻的这套架构,它写出来的东西大概率是编译都过不了。
而且虚幻的API命名和参数传递有很强的约定性。比如你想让一个Actor动起来,你得知道用SetActorLocation还是AddActorLocalOffset,得知道DeltaTime要从Tick里拿,得知道PrimaryActorTick.bCanEverTick要设成true。这些细节,模型如果没在训练数据里见过足够多的虚幻项目,它就只能靠猜。猜对了是运气,猜错了就是一堆编译错误。
提示:虚幻引擎的C++编译错误信息往往非常长,一个模板实例化失败能给你刷出上百行报错。模型如果不能在第一次就写出结构正确的代码,后续让它“根据报错修复”的成功率会急剧下降,因为它根本分不清哪些报错是根因,哪些是连锁反应。
2.2 同题异构比“跑分”更能看出真实水平
传统的模型评测,比如MMLU、HumanEval这些,本质上是“选择题+填空题”。模型只要在某个知识点上见过类似的题,就能拿分。但做游戏是一个从零到一的构建过程,没有标准答案,也没有选项给你排除。
同题异构的好处在于,所有模型面对的是完全相同的需求描述、完全相同的引擎版本、完全相同的编译环境。变量控制住了,剩下的差异就是模型本身的差异。这比让每个模型做不同的题然后比分数要公平得多。
我举个具体的例子。假设需求是“做一个第一人称的收集物小游戏,玩家移动、跳跃,碰到旋转的方块就得分,得分到10就胜利”。这个需求里至少包含了:角色移动组件配置、输入映射、碰撞检测、Actor旋转逻辑、分数UI更新、胜利条件判断。每个环节都有多种实现方式,模型选哪种、为什么选那种、代码组织得好不好,全都能看出来。
2.3 12个模型的梯队划分逻辑
虽然我拿不到那12个模型的具体名单,但根据圈子里流传的评测结果和我在类似任务上的经验,这12个模型大致可以分成四个梯队:
| 梯队 | 特征 | 典型表现 |
|---|---|---|
| 第一梯队 | 一次编译通过,逻辑完整,有基本的代码组织 | 能跑能玩,偶有小bug但不影响核心体验 |
| 第二梯队 | 需要1-2轮修复,核心功能可用 | 能跑,但某些边界情况会出问题 |
| 第三梯队 | 需要大量人工干预,代码结构混乱 | 勉强能编译,但玩起来体验很差 |
| 第四梯队 | 编译都过不了,或者逻辑完全跑不通 | 翻车,需要重写 |
最贵的那个模型落在第四梯队,这事儿其实并不罕见。我后面会详细拆它翻车的原因。
3. 12个模型交出来的东西到底差在哪
3.1 第一梯队的共同特征:懂引擎的“规矩”
第一梯队的模型,交出来的代码有一个很明显的共同点:它们知道虚幻引擎的项目该怎么组织。不是那种“把所有逻辑塞进一个Actor里”的写法,而是会主动拆分模块。比如把输入处理放在PlayerController里,把游戏规则放在GameMode里,把UI更新放在HUD里。
这种组织方式的好处是,代码的可读性和可维护性都上了一个台阶。更重要的是,这种结构化的写法意味着模型对虚幻的架构有真正的理解,而不是在“背代码”。
我印象比较深的是,第一梯队的模型在处理“得分到10就胜利”这个逻辑时,会用一个UPROPERTY(BlueprintReadOnly)的变量来存分数,然后在GameMode里写一个CheckWinCondition函数,在每次加分后调用。这种写法很“虚幻”,也很稳。而低梯队的模型,往往是在Tick里每帧判断一次分数,虽然也能跑,但逻辑上就粗糙很多。
3.2 第二梯队的典型问题:能跑但不够“讲究”
第二梯队的模型,核心功能基本都能实现,但总有一些地方让你觉得“差口气”。最常见的问题是输入映射的配置不完整。比如它知道要绑定MoveForward和MoveRight,但忘了在项目设置里把输入轴映射加上,导致代码编译通过但运行时角色不动。
还有一个高频问题是碰撞通道设置错误。虚幻的碰撞系统有Block、Overlap、Ignore三种响应,还有各种预设的碰撞通道。模型如果只是随便设一个BlockAll,可能会导致角色被自己的收集物挡住,或者收集物之间互相碰撞导致物理爆炸。
注意:虚幻引擎的碰撞问题在编辑器里跑的时候可能看不出来,但打包之后行为会不一样。如果你在做类似的评测,一定要把“打包后运行”作为验收标准之一,不能只在编辑器里PIE跑一下就完事。
3.3 第三梯队的通病:代码能编译,但玩起来是灾难
第三梯队的模型,代码往往能通过编译,但运行起来问题一大堆。我见过最典型的几个:
- 角色移动速度单位搞错。虚幻默认的单位是厘米,
MaxWalkSpeed设成600是正常走路,设成60000就是瞬移。模型如果没这个概念,直接填个“看起来合理”的数字,角色就会像火箭一样飞出去。 - 旋转逻辑用错函数。想让方块旋转,应该用
AddActorLocalRotation或者SetActorRotation配合FRotator,但有些模型会去改RootComponent的RelativeRotation,结果就是旋转轴完全不对。 - UI更新绑定错事件。分数变化了但UI不刷新,因为模型把更新逻辑写在了
BeginPlay里,而不是在分数变化的那个时刻触发。
这些问题单个看都不致命,但凑在一起,游戏就没法正常玩了。
3.4 最贵模型翻车的三个致命伤
现在来说说最贵的那个模型。根据流传出来的信息,它翻车主要翻在三个地方:
第一,它试图“炫技”而不是“解决问题”。这个模型生成的代码里用了很多高级特性,比如自定义的UObject派生类、复杂的委托绑定、甚至还有模板元编程的痕迹。但问题是,这些高级特性在虚幻的反射系统里有很多限制,模型显然没有完全掌握,导致编译时一堆Unresolved External Symbol。
第二,它对虚幻的垃圾回收机制理解有误。它把一些UObject指针用裸指针存了,没有加UPROPERTY()宏。这在C++层面没问题,但虚幻的GC不会追踪这些引用,运行一段时间后对象被回收,程序直接崩溃。这个错误非常隐蔽,编辑器里跑几秒钟可能看不出来,但玩久了必崩。
第三,它的错误修复能力反而更差。因为它的代码结构太复杂,当编译报错时,它给出的修复建议往往是“在错误的地方打补丁”,而不是“重构有问题的部分”。结果就是越修越乱,最后连原始需求都实现不了了。
提示:模型在代码生成任务上的表现,跟它的“代码风格”有很大关系。有些模型倾向于生成简洁直接的代码,有些则喜欢堆砌设计模式。在做工程任务时,简洁直接的代码往往更容易调试和修复。
4. 如果让你来设计这个天梯评测
4.1 需求描述的写法直接决定评测上限
我看了这个评测的需求描述之后,最大的感受是:需求写得好不好,直接决定了模型能不能发挥出真实水平。如果需求写得太模糊,比如“做一个收集游戏”,那模型只能靠猜,猜出来的东西五花八门,根本没法横向对比。如果需求写得太细,比如“用ACharacter类,在BeginPlay里生成10个ACollectibleActor,每个Actor的旋转速度是90度每秒”,那又变成了“填空题”,测不出模型的架构能力。
好的需求描述应该像一份产品需求文档:说清楚玩家能做什么、游戏规则是什么、胜利和失败条件是什么,但不说具体怎么实现。比如:
- 玩家以第一人称视角移动,可以前后左右走,可以跳跃。
- 场景中随机分布10个旋转的方块。
- 玩家碰到方块,方块消失,分数加1。
- 分数达到10时,屏幕上显示“胜利”。
- 玩家掉出地图边界时,回到起点,分数清零。
这种描述既给了模型足够的约束,又留出了实现空间。
4.2 验收标准要分层次,不能只看“能不能跑”
很多评测只关注“能不能编译、能不能跑”,但这远远不够。我建议把验收标准分成四个层次:
- 编译通过:代码能在指定引擎版本下无错误编译。
- 核心功能可用:玩家能移动、能收集、分数能变化、胜利条件能触发。
- 边界情况正确:掉出地图能重置、重复碰撞不会重复加分、UI刷新及时。
- 代码质量达标:没有明显的GC隐患、没有硬编码的魔法数字、模块划分合理。
只有四个层次都过了,才能算“第一梯队”。只过前两个的,最多算第二梯队。
4.3 评测流程的标准化步骤
如果你自己想搞一个类似的评测,我建议按这个流程来:
- 固定引擎版本和项目模板。所有模型都用同一个版本的虚幻引擎,从同一个空白C++项目开始。
- 统一需求描述。把需求写成一份文档,所有模型看到的内容完全一致。
- 限制交互轮次。比如最多允许模型进行3轮“生成-编译-修复”的循环,超过3轮还没搞定就算失败。
- 记录每轮的错误信息。把编译错误、运行时报错、崩溃日志都保存下来,方便后续分析。
- 人工验收。找一个不懂AI但懂游戏的人来试玩,记录他的主观体验。
这套流程跑下来,大概需要两到三天的时间,但得到的结果比任何跑分都更有参考价值。
5. 从翻车案例里能学到什么
5.1 模型选型的核心指标不是价格
这次评测给我最大的启发是:选模型不能看价格,要看它在你的具体任务上的表现。一个模型可能在写Python脚本上很强,但到了C++和游戏引擎这种强类型、强架构的环境里,它的优势可能完全发挥不出来。
我自己的经验是,选模型之前一定要做小样本实测。拿你最核心的那个任务,让候选模型各做一遍,看谁一次通过率最高、谁的代码最好改。这个实测的成本很低,但能帮你省下大量的后期调试时间。
5.2 代码生成任务的“隐藏成本”在修复环节
很多人评估模型的时候只看“第一次生成的质量”,但实际工程中,修复成本往往比生成成本更高。一个模型如果第一次生成的代码有10个错误,但它能根据报错信息快速定位并修复,那它的实际效率可能比一个“第一次只错3个但修不好”的模型更高。
所以评测的时候,一定要把“修复能力”作为一个独立维度来打分。具体可以看:修复后的代码是否引入了新问题、修复轮次是否收敛、修复建议是否合理。
5.3 虚幻引擎对AI代码生成的特殊挑战
虚幻引擎有几个特性,让它特别不适合“随便生成代码”:
- 反射系统:
UCLASS、UPROPERTY、UFUNCTION这些宏不是可选的,写错了就是编译错误或者运行时崩溃。 - 垃圾回收:不正确的指针使用会导致随机崩溃,而且崩溃点往往离错误点很远。
- 蓝图与C++的交互:很多功能在蓝图里很简单,但在C++里需要额外的宏和配置。
- 编译时间长:每次修改都要等编译,这大大降低了“试错”的效率。
这些特性意味着,模型如果对虚幻没有足够深的理解,它的“试错”成本会非常高。这也是为什么第一梯队的模型往往是在虚幻相关代码上训练得更充分的那些。
6. 实操中怎么避坑
6.1 给模型的提示词要“像给实习生写任务”
如果你要用模型来生成虚幻代码,提示词的写法很关键。我的经验是,把模型当成一个刚入职的实习生:它懂基本的C++,但对你的项目结构不熟。所以提示词里要包含:
- 项目的基本结构(有哪些主要的类,各自负责什么)。
- 命名规范(比如Actor前缀用A,组件前缀用U)。
- 必须遵守的约束(比如所有UObject指针必须加UPROPERTY)。
- 一个简单的示例(比如“参考这个Actor的写法”)。
这样模型生成的代码,一次通过率会高很多。
6.2 编译错误的处理策略
当模型生成的代码编译报错时,不要直接把所有报错都扔给它。虚幻的报错信息经常是“连锁反应”,第一个错误会导致后面几十个错误。正确的做法是:
- 先看第一个错误,让模型只修复这一个。
- 修复后重新编译,再看新的第一个错误。
- 重复这个过程,直到编译通过。
这样做虽然轮次多,但每次修复的目标明确,不容易越修越乱。
6.3 运行时的崩溃排查
如果代码编译通过但运行时崩溃,优先检查这几个地方:
- 空指针:模型生成的代码经常忘记检查
GetWorld()、GetController()这些可能返回空的调用。 - GC问题:检查所有
UObject指针是否都有UPROPERTY()宏。 - 数组越界:模型在遍历数组时,经常忘记检查索引范围。
- 除零错误:在计算比例、速度这些东西时,分母可能是0。
虚幻的崩溃日志会给出调用栈,但调用栈里的函数名可能是引擎内部的,需要结合你自己的代码来定位。
6.4 验收时的“魔鬼细节”
最后验收的时候,有几个细节特别容易出问题,但特别影响体验:
- 输入延迟:如果模型把移动逻辑写在
Tick里但没有用DeltaTime,角色移动速度会跟帧率挂钩,高帧率下快得飞起。 - UI刷新时机:分数变化后UI不刷新,往往是因为模型用了
Tick来更新UI,但更新条件写错了。 - 碰撞体积:收集物的碰撞体积如果太大,玩家还没碰到就得分了;太小又碰不到。
- 重置逻辑:掉出地图后重置,如果忘记重置速度,角色会带着掉落时的速度重生,直接飞出去。
这些细节,模型在生成代码时往往不会主动考虑,需要你在验收时逐项检查。
7. 这个评测对AI应用开发的启示
7.1 模型能力边界比模型排名更重要
这次评测最有价值的地方,不是排出了谁第一谁倒数,而是暴露了每个模型的能力边界。有的模型擅长写逻辑,但不擅长处理引擎特有的约束;有的模型代码风格好,但边界情况考虑不周;有的模型修复能力强,但第一次生成的质量堪忧。
知道这些边界之后,你在实际项目中就能扬长避短。比如用A模型生成核心逻辑,用B模型做代码审查,用C模型写单元测试。这种组合拳往往比死磕一个“最强模型”更有效。
7.2 工程任务需要“领域适配”的模型
通用大模型在通用任务上表现好,不代表它在你的领域任务上表现好。虚幻引擎是一个高度专业化的领域,有自己的一套术语、架构和最佳实践。模型如果没在这个领域里“泡”过足够多的数据,它的表现就会大打折扣。
所以选模型的时候,一定要看它在你的目标领域上的表现,而不是看它在通用榜单上的排名。
7.3 人工介入仍然是必要的
不管模型多强,在虚幻引擎这种复杂工程环境里,完全无人值守的代码生成还是不现实的。模型可以帮你写出70%到80%的代码,但剩下的20%到30%——那些涉及引擎特性、性能优化、边界处理的代码——还是需要人来把关。
我的建议是,把模型当成一个“高级代码补全工具”,而不是“自动程序员”。它能帮你省掉大量敲键盘的时间,但架构设计和关键决策还是得你自己来。
8. 我个人的一些实操体会
我在自己的项目里也试过用不同模型生成虚幻代码,踩过的坑跟这个评测里说的基本一致。最深的体会是:不要迷信“最强模型”。有些模型在写Python的时候确实很惊艳,但到了C++和虚幻引擎,它的表现可能还不如一个中等水平的模型。
另外,模型的“性格”也很重要。有些模型倾向于生成“教科书式”的代码,结构清晰但啰嗦;有些模型喜欢走捷径,代码短但可读性差。在团队协作的场景下,我宁愿要一个代码风格稳定、可读性好的模型,哪怕它第一次生成的代码需要多改几处。
最后分享一个小技巧:如果你要用模型生成虚幻代码,先把你的项目模板和常用类的头文件喂给它。这样它生成的代码会更贴合你的项目风格,减少后续的适配成本。这个操作花不了几分钟,但能显著提升生成代码的可用性。
这个天梯评测后续还可以往几个方向扩展:比如加入“多人协作”场景,看模型能不能处理网络同步;或者加入“性能优化”环节,看模型生成的代码在大量Actor场景下帧率表现如何。这些方向都比单纯的“能不能跑”更有实际参考价值。