上周末我想试试手里这个顶级大模型到底有多能打,随手就给它丢了一句话:"用HTML加JavaScript写一个类似QQ飞车的赛车小游戏,要能玩。"说完我就去倒水了,回来发现屏幕上多了一个完整的HTML文件。双击打开,一个俯视视角的赛车游戏居然真的能跑:左右切道、加减速、躲车、撞车结束、重新开局,一气呵成。这篇就把这次完整的AI写游戏过程拆开聊聊:提示词怎么设计才容易被顶级模型理解、生成出的代码结构怎么拆、拿到手之后怎么调优,以及我实际踩过的几个坑。如果你是AI编程新手,或者想用AI快速做原型验证,这篇应该能给你一套可以直接复制的打法。
1. 为什么拿"一句话写赛车游戏"来考验AI
1.1 小游戏恰好是大模型的"综合素质考试"
很多人用AI写代码都停留在"帮我写个函数""修个bug"这种级别,这些任务其实测不出模型的真实水平。真正能看出大模型代码能力强不强,得让它做一个需要多个模块协作的东西。赛车小游戏就是这样一个理想考题:它要Canvas绘制、游戏主循环、键盘事件、状态管理、碰撞检测、随机生成、UI交互,甚至还要一点简单的动画调度。
这些模块不是孤立的。主循环里既要更新数据又要渲染画面,键盘事件要改玩家状态,碰撞检测要读取车辆坐标做判断,游戏结束要把状态切回去。哪怕任何一个环节断了,游戏要么不动、要么闪退、要么根本画不出来。所以模型能一次写出这种闭环逻辑,说明它不是靠记忆拼接代码片段,而是真的理解了"游戏循环"是怎么转起来的。
我在实际测试里还有个观察:基础模型往往能写出漂亮的静态页面代码,但一遇到"玩家移动""循环刷新"这种动态逻辑就开始露怯,要么只画一帧,要么死循环让浏览器卡死。顶级模型之所以表现好,是因为它对"状态如何随时间变化"这件事的建模能力更强,这在生成游戏时几乎是生死线。
1.2 为什么选"QQ飞车"这个题材
先说清楚,这里说的QQ飞车不是腾讯那个端游或手游,只是借用它的品类特征做个技术练习demo,代码和素材都是AI现写的,跟原作没有任何关系。选这个题材,是因为赛车玩法非常非常适合验证AI的游戏生成能力,具体有三点考虑。
第一,玩法核心够简单。赛车游戏最基础的动作就是"移动+躲避",控制逻辑只有左右转向、加减速,但反馈感极强。这种简单物理结构是最适合AI一次生成成功的,换成RPG、FPS这种复杂题材,几百行代码根本撑不起可玩的体验。
第二,画面表现容易做。2D俯视赛车根本不用复杂的Spritesheet,用Canvas画几个圆角矩形、几条车道线,再配合道路滚动,就能模拟出速度感。模型不需要处理大量美术资源,可以把全部能力集中在游戏逻辑上。
第三,竞速题材的成就感天然高。玩家看到速度表在涨、里程在加、惊险躲过一辆车,这种正向反馈是写代码的人很难用形式化方法描述的体验,但AI生成的游戏天然具备这种底层乐趣。
1.3 先摆正预期:别指望AI一次成型
我得给你打个预防针:标题说"一句话AI写出来",实际是流量性说法。真实过程是"一段话给需求,三到五轮对话迭代",最终得到能玩的版本。第一版生成的游戏大概率能跑,但手感会比较粗糙,车没什么惯性,碰撞判定的范围偏大,敌车速度也一成不变。
这不是模型不行,而是提示词天然缺信息。你脑子里对"好玩"的体感,如果不拆成具体参数写进去,模型就只能给一个中规中矩的默认方案。所以这篇文章后面会专门讲:拿到第一版之后,怎么通过第二轮、第三轮提示词把"不能玩"变成"有点意思"。预期放对了,后面每一步都不会别扭。
2. 选模型、写提示词:那"一句话"到底说了啥
2.1 我用的顶级模型和一次横向对比
这次我主力用的是Claude 3.5 Sonnet,当时体验下来代码完成度确实是最高的;手边也拿DeepSeek、豆包做了同样的测试。直接说结论:顶级模型和普通模型在生成游戏这种任务上的差距不是一点点,主要体现在三处。
第一,一次生成的完整度。顶配模型会主动把开始界面、游戏循环、结束重开这些状态全部写完,而有些模型会莫名其妙漏掉"重新开始"按钮的逻辑,一旦游戏结束就只能刷新页面。第二,Canvas绘制的细节。画道路飘移效果、车体圆角、仪表盘布局,顶级模型会加不少很懂视觉的细节,普通模型经常是几个光秃秃的色块。第三,bug密度。实测下来顶级模型的代码基本能一次跑通,其他模型可能需要看Console报错再改两三轮。
如果你手头没有同款模型,也不用慌,下面整篇文章的思路完全通用。大模型对提示词的理解逻辑是相似的,你只要把需求写清楚了,国产模型也能给出能玩的版本,只是迭代轮数可能多一点。
2.2 完整提示词原稿逐项拆解
我第一次输入给模型的提示词原文是这样的,你直接复制就能用:
用HTML + CSS + JavaScript写一个类似QQ飞车的赛车小游戏,用Canvas实现,要求: 1. 俯视视角,3车道公路,道路向下滚动,营造向前飞驰的感觉 2. 玩家赛车在画面底部,可用左右方向键切换车道、上下键控制加减速 3. 路上有其他车辆从上方出现,往下行驶,玩家需要躲避 4. 撞到其他车辆后游戏结束,页面中央显示最终得分和"重新开始"按钮 5. 画面要有仪表盘显示当前速度、得分、行驶距离 6. 配色要像QQ飞车那样鲜艳明快,车是卡通风格 7. 所有代码放在一个html文件里,双击即可在浏览器运行这里面每一句都不是废话,我来逐条拆给你看。
第1条"俯视视角、3车道公路、道路向下滚动",直接锁死了游戏形态。如果你不写视角,模型有各种选择,可能是横版、可能是3D伪透视,后面你改起来会很难受。第2条明确控制方式,把交互模型定死,避免它做个鼠标点击的版本出来。第3和第4条是核心循环:敌车出现、玩家躲避、碰撞结束、能重开。这四条一旦缺失,游戏就等于没做。
第5条关于仪表盘的细节是我刻意加的,它逼着模型维护"速度""得分""距离"三套数据,等于强制它把游戏状态做得更完整。第6条是审美引导,AI默认配色往往很素,加了"鲜艳明快"之后生成效果会好一个档次。最后第7条最关键,"一个html文件、双击可运行"——这是交付约束。如果不写这条,它可能给你返回一个带Node.js依赖的项目,你就没法直接玩了。
2.3 提示词设计的三个底层原则
拆完具体提示词,再往上抽象一步,你会发现想让AI好好干活,本质上就是三件事。第一,给边界而不是给答案。你不需要教它怎么画车、怎么写碰撞函数,你只需要说清楚"3车道、俯视、向下滚动"这些约束,它自然会用最合理的实现去满足你。第二,把完成标准写明白。什么叫"能玩"?就是能开始、能操作、能结束、能重开。把这四条写进提示词,模型才不会交出半成品。第三,第一次只聚焦核心玩法。别上来就要求漂移、氮气、音效、道具、排行榜,需求越多,模型越容易在细节上翻车。先把基础驾驶和碰撞做到位,第二三轮再慢慢加花活。
这第三条我特别想强调。第一版我要是写"类似QQ飞车,要有氮气、漂移、道具赛",模型大概率会生成一个看起来功能很多但哪个都不好用的东西。不如先把骨架打牢,后面用迭代对话去加功能,反而更快。
3. 拆解AI生成的代码:为什么它真的能玩
3.1 一个HTML文件里装了什么
AI返回的是一个完整的HTML文件,看起来大概六七百行,结构分成四块:HTML骨架、CSS样式、Canvas画布和JavaScript逻辑。HTML骨架里除了canvas,还有开始界面的遮罩层、结束界面的得分面板、重新开始按钮;CSS负责把页面居中、给按钮做样式、把仪表盘固定在顶部;JavaScript是重头戏,被模型组织成三个区域:全局状态、更新逻辑、渲染逻辑。
这种组织方式很聪明,跟专业游戏开发的分层思想是一致的。全局状态里放了玩家对象、敌车数组、当前速度、得分、游戏是否结束这些变量。更新逻辑负责改变状态,比如按下右键让player.x变大,敌车每帧往下移几像素。渲染逻辑只负责把当前状态画到Canvas上。状态变了,画面跟着变;循环转起来,游戏就活了。
我还注意到AI自己加了不少代码注释,每个区块上面都写了中文注释说明功能,这对后面调试非常有帮助。拿到代码别急着跑,先大概扫一眼结构,你就知道该去哪儿改参数了。
3.2 游戏主循环与核心机制拆解
整个游戏的心脏是requestAnimationFrame驱动的gameLoop,AI生成的核心逻辑精简后大概是这个样子:
let player = { x: 300, y: 600, w: 50, h: 80, speed: 5 }; let enemies = []; let score = 0; let gameOver = false; let roadOffset = 0; function update() { // 道路滚动,模拟前进 roadOffset += player.speed; // 玩家受键盘控制 if (keys['ArrowLeft'] && player.x > 30) player.x -= 6; if (keys['ArrowRight'] && player.x < canvas.width - 80) player.x += 6; if (keys['ArrowUp']) player.speed = Math.min(player.speed + 0.1, 12); if (keys['ArrowDown']) player.speed = Math.max(player.speed - 0.1, 3); // 随机生成敌车 if (Math.random() < 0.02 && enemies.length < 6) { enemies.push(createEnemy()); } // 敌车移动 enemies.forEach(e => e.y += e.speed); // 碰撞检测 enemies.forEach(e => { if (isCollide(player, e)) { gameOver = true; showGameOver(score); } }); // 计分 score += player.speed * 0.1; } function render() { ctx.clearRect(0, 0, canvas.width, canvas.height); drawRoad(); // 画路面、车道线、路肩 drawPlayer(); // 画玩家的车 drawEnemies(); // 画敌车 drawUI(); // 画速度、得分、里程 } function gameLoop() { update(); render(); if (!gameOver) requestAnimationFrame(gameLoop); }你会发现这套逻辑非常标准。update负责算,render负责画,gameLoop把两者绑在一起反复跑。碰撞检测用的是矩形相交判断,模型会在玩家车和敌车外面各留一点安全边距,用两组坐标做区间比较,这样视觉上还没碰到就算撞了,反而很合理。
有个细节值得提:模型把敌车生成概率控制成了"每帧2%概率、总量不超过6辆",避免一下子刷出一堆车导致没法玩。这种参数不是提示词里要求的,是模型自己根据游戏常识调出来的,这就是顶级模型的厉害之处。
3.3 运行前置条件与"能玩"判定标准
运行这个游戏没有任何门槛,你只需要把AI输出的HTML代码复制到文本文档里,保存成index.html,然后双击用浏览器打开就行。不需要装Node、不需要启动服务器、不需要下载任何依赖,因为所有代码都在同一个文件里。
我建议你拿到文件后按四条标准快速验证一遍:打开页面能看到开始界面;按空格或回车能开始游戏;左右键能控制车辆移动、上下键有效果;撞车后弹出结束面板且"重新开始"按钮能正常重置游戏。这四条全过,就说明已经达到了"能玩"的最低标准。后面想更好玩,就进入迭代调优阶段。
4. 实操全流程:从生成到跑顺
4.1 完整步骤:复制、保存、打开、试玩
具体操作流程没有什么玄学,我把它拆成四个动作。第一步,在AI对话框里输入前面那段提示词,等它生成代码。第二步,点代码块右上角的复制按钮,把完整HTML代码复制下来。注意我强调过,一定要等它把内容全部输出完再复制,有些模型长代码会被截断,你可别只复制了一半。第三步,打开系统自带记事本(或者VS Code这种编辑器),粘贴代码,另存为index.html。这里有个坑:文件编码一定要选UTF-8,否则网页里的中文注释和按钮文字会变成乱码。第四步,双击html文件,默认浏览器就会把它打开,游戏直接启动。
如果你想改游戏,不用在网页里改,随时回到编辑器改代码里的参数再保存、刷新浏览器即可。整个循环就是我说的"改代码-刷新-试玩"三秒一轮,效率非常高。
4.2 第一次试玩实录:哪里能用、哪里闹心
我是抱着"肯定能跑但一定很糙"的心态去试的。说实话第一版能跑,还挺惊喜,但玩了半分钟就发现了几个明显的问题。第一个是车的操控手感非常硬,左右键一按车就跳一个车道,没有中间过渡,总感觉像"瞬移"而不是"转弯"。第二个是碰撞判定偏大,明明看起来还有半个车身的距离,居然就判定撞上了,体验上像被空气撞了。第三个是敌车速度恒定,从头到尾一个样,刚开始觉得还行,玩到后面就有点无聊。
这些问题本质上不是bug,而是参数设置问题。AI给了一个默认值,但默认值不等于你玩着舒服的值,所以下一步就是针对性调参。
4.3 三轮迭代调优:手感、难度和性能
第一轮先调手感。我给它发了一句:"现在车的移动太生硬,希望有加速度和摩擦力,左右移动时车体有倾斜效果,看起来更像转弯。"模型很快改出了带惯性的版本:玩家车不再瞬间移动,而是有一个x方向的速度,再通过加减速度来控制,同时增加了倾斜角度的绘制。这一轮改完,操控立刻舒服了很多,有了点真实车的感觉。
第二轮调难度曲线。我要求"敌车速度随游戏时间逐渐升高,而且当玩家分数超过500时,生成敌车的概率增加"。模型做了个动态难度系统:游戏全局记录elapsedTime,用它去乘一个基础速度系数,同时还把生成概率从0.02逐步提高到0.04。改完之后游戏就变成了"越玩越刺激",分数高起来的紧迫感非常明显。
第三轮是性能优化。虽然一个小游戏不至于卡顿,但我观察到玩久了内存可能会涨,因为敌车数组只增不减。我提醒模型"离开屏幕的敌车要销毁,避免数组无限变大",它马上在移动逻辑里加了过滤条件,把y大于canvas.height的敌车从数组里移除。这个改动虽然玩家感知不到,但页面稳定性提升很明显,长时间挂着也不会发烫。
三轮改完,这个"一句话"生成的游戏已经达到了能拿得出手的水平,发到群里被朋友问了好几次"在哪玩的"。
5. 踩坑记录与继续扩展的方向
5.1 常见问题速查表
这几次折腾下来,我把最容易遇到的一批问题整理成了一个表格,你照着排查就行。
| 现象 | 原因 | 解决办法 |
|---|---|---|
| 页面打开一片黑 | JS运行时错误,最常见是代码被截断 | 打开浏览器F12看Console,直接让AI修复报错行 |
| 按方向键没反应 | 键盘事件监听的不是window,或按键冲突 | 让AI检查addEventListener绑定的对象,统一改为window |
| 按键一直加速停不下来 | 只写了keydown没写keyup,状态没复位 | 让AI在keyup里释放对应按键的状态 |
| 没碰到车也算撞 | 碰撞盒比车体大太多 | 让AI把碰撞盒的判定区域缩小,留5-10px余量即可 |
| 游戏结束点击重开无效 | 重置函数没有把数组和分数清空 | 让AI检查重置逻辑,确保enemies数组、score、gameOver全部归零 |
| 中文显示成了方块 | 文件编码不是UTF-8 | 用编辑器另存为时选UTF-8,或补上meta charset="UTF-8"标签 |
对照着这个表,绝大多数翻车现场都能在十分钟内救回来。我遇到过最离谱的一次是模型生成的代码里有一个多余的"}"没闭合,整个脚本直接挂掉。当时我第一反应是让AI自己检查,它一眼就找出来了,这也算AI调试AI的典型场景。
5.2 几个多数人不注意的避坑细节
第一个细节,控制好对话上下文。如果你在同一个对话里连续生成好几个版本的代码,模型可能会被之前的旧代码干扰,生成缝合怪。我建议每轮迭代都明确说"基于上一个版本修改,只改我要求的部分"。第二个细节,尽量不要在同一个提示词里混合太多冲突要求,比如"又要有漂移手感又要车辆完全贴地",AI会做出很奇怪的妥协方案。第三,每次试玩前,关闭浏览器旧的标签页重新打开,避免旧JS缓存在内存里干扰新代码。
还有一点我觉得很实用:让AI帮你写代码的时候,让它顺带写清楚"关键参数都放在文件顶部"。这样你之后改手感、改难度都只需要动文件头部那几行,不用在一堆代码里像大海捞针一样找参数。
5.3 还能往哪里扩展
游戏能玩了,这只是起点。后面的扩展空间其实非常大,我列几个我个人觉得特别适合继续折腾的方向。
第一个方向,加游戏系统。氮气加速、漂移蓄力、道具箱子、多张地图、AI对手车,这些都是QQ飞车类玩法的经典元素,每个都可以单独作为一个主题再去让AI实现。比如你让它"加一个氮气系统,按住Shift消耗能量加速,能量条通过漂移获得",这又是一个完整的迭代故事。
第二个方向,把AI写的小游戏当原型,再去接真实游戏引擎。如果你真有做完整游戏的打算,可以用这个HTML版本当需求文档,把玩法和参数梳理清楚,再用Godot或Unity重做,效率会高很多。第三个方向是往AI Agent方向走,把"提示词写游戏"变成"Agent自主调优游戏参数",让大模型自己跑试玩、自己分析手感并修改代码,这套思路已经有了实践雏形,但那就是另一篇长文了。
最后分享一个我个人的体会:顶级模型的价值不在于"替你写一个项目",而是帮你在几分钟内把模糊的想法变成一个可以上手试错的运行体。从"一句话到能玩",真正的重量不在这句话,而在你怎么把脑子里的"好玩"翻译成模型能理解的"参数和规则"。这个翻译能力,才是AI时代最值得练的基本功。建议你也找个周末,拿身边的大模型试试,先从一句"帮我写个能玩的赛车游戏"开始。