前两天在 GitHub 丢了个仓库,README 第一行写着:A mini RTS game inspired by Red Alert, playable & open source. Built with GPT 6.1.朋友看到后第一反应基本都是:你真让 AI 写了个红警?能玩吗?
我说能,而且不是 GIF 演示那种“能玩”,是能选单位、能采矿、能造兵、能派坦克去怼对面基地那种“能玩”。仓库放出去没几天,陆陆续续有人 Star,有人提 issue,甚至有个老哥直接提了 PR 加音效。这篇文章就把整个过程拆开讲讲——从技术选型、系统设计,到怎么一步步让大模型把代码吐出来,再到开源之后踩到的坑。想用 AI 辅助写小游戏的朋友,这篇应该能帮你少走不少弯路。
1. 先把话说清楚:这个“红警”到底是怎么来的
1.1 项目由头和最终成品形态
事情得从一次闲聊说起。当时我在群里吹水,说现在大模型写代码这么强,能不能让它从零搓一个即时战略游戏出来?大家起哄说有本事你真做。我平时主要写业务代码,对游戏开发属于“玩过但没写过”的水平,正好借这个机会试试水。
于是我就开始了这个项目:用对话式大模型作为主力编码工具,目标是在两周内做出一个能在本地跑起来的迷你红警。最终产出的游戏规模不大,但五脏俱全:
- 地图是 64×64 的格子地图,每个格子 32 像素,整体窗口 1280×720。
- 玩家能造电厂、兵营、矿场和坦克厂。
- 有采矿车自动采资源,有步兵、火箭兵和坦克三个作战单位。
- 敌方 AI 会自己发展经济、造兵、集结,然后分波次来打你。
- 有简单的战争迷雾,视野外的地方看不见,探索过但没视野的地方显示灰色。
- 打赢条件是摧毁敌方基地,打输条件是己方基地被拆。
说白了,这就是一个把红警的“资源—建造—战斗”核心循环压缩到最小可行版本的东西。代码总共大概 5000 多行,纯 Python,依赖只有一个 Pygame。仓库名字叫ra-mini,目前是 MIT 许可证,你要是想拿去改、拿去玩,完全没限制。
1.2 为什么叫 GPT 6.1
这个得先解释一下,免得有人较真。标题里说的“GPT 6.1”其实是我跟朋友之间的调侃说法,意思是“当时手头能用到的最强对话模型”,并不是说真有某个官方版本叫 6.1。圈子里朋友之间吹水,习惯把“比上一版强一点”的模型叫作x.1,叫着叫着就叫顺口了。
所以下文提到 GPT 6.1 的时候,你就把它理解成“当时那个负责写代码的大模型”就行。真正有意思的不是我用了哪个精确版本,而是我和它之间的协作方式:我把需求拆碎,它负责快速产出可运行的代码,我再验收、提问、让它修。整个开发过程里大概有七八成代码是它写的,但架构判断和关键取舍全在我这边。
1.3 它适合谁,你能从里面拿走什么
这个项目对三类人可能比较有用:
第一类是像我一样想用 AI 辅助做点东西的开发者,你可以看看怎么把一个大需求拆成一堆小 Prompt,让模型持续产出靠谱代码;第二类是 RTS 爱好者,哪怕不写代码,看看迷你红警的模块拆解也挺有意思;第三类是刚学 Python 的人,这份代码是很好的游戏编程入门样本,单位类怎么写、地图数据怎么组织、主循环怎么跑,都有比较清晰的示范。
我很反感那种“AI 一键生成整个项目”的吹法,实际情况根本不是这样。后面你会看到,AI 写代码的过程更像是在和一个记忆力超强但偶尔犯傻的同事结对编程,你要不停给它反馈、给它兜底,最后才能把东西磨出来。
2. 技术选型拆解:为什么是 Python + Pygame
2.1 备选方案对比:Unity、Web 还是 Pygame
动手之前我也纠结了一下技术栈,毕竟 RTS 游戏可大可小,选错了后面全是坑。我当时列了三个方向,测下来各自的优缺点非常明显:
| 方案 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| Unity + C# | 引擎成熟,UI、物理、动画都有现成方案 | 项目结构重,打包麻烦,AI 生成的 C# 代码经常得改很多才能跑 | 做商业化游戏、需要跨平台发布的场景 |
| Web + Canvas/TypeScript | 部署方便,扔到网页就能玩 | 处理游戏循环、输入、性能问题时要额外照顾浏览器行为 | 想分享链接让别人直接玩 |
| Python + Pygame | 依赖极简,语法直接,AI 训练数据里这类代码特别多 | 性能上限低,单位数量过千就会卡 | 原型验证、学习练手、快速出结果 |
我最后选了 Python + Pygame,最重要的理由是:这个项目本身是“验证 AI 能不能帮我做游戏”的实验,应该先把不确定性降到最低。Pygame 的代码模式非常固定——初始化窗口、写主循环、处理事件、更新逻辑、画图,这套东西在互联网上有海量样本,大模型对它的熟悉程度远高于对一套复杂引擎的熟悉程度。
2.2 游戏主循环和渲染的基本框架
Python + Pygame 的经典结构其实是所有游戏的地基:一个 while 循环,每帧做三件事——处理用户输入、更新游戏逻辑、把画面画出来。帧率固定在 60 FPS,也就是每秒钟循环 60 次。
这个框架看着简单,但它对后续所有功能都有约束力:单位移动要按帧来算位移,攻击冷却要按帧数来计数,动画也要靠帧来切。我们的代码里主循环长这样:
while running: dt = clock.tick(60) / 1000.0 handle_events() update(dt) draw(screen)dt是上一帧到这一帧的时间差,单位是秒。早期版本我没传这个参数,结果不同电脑帧率不一样,游戏速度也忽快忽慢。后来统一用dt做时间基准,单位速度不再按每帧固定像素走,而是按每秒像素数乘上dt,这才稳定下来。这个经验虽然基础,但如果你没用 Pygame 做过东西,十有八九会在这里栽一次。
2.3 代码组织方式
一个 RTS 游戏最怕的就是把所有逻辑堆在一个文件里。几千行代码放一起,AI 后面根本没法接上下文,我自己改起来也难受。所以项目从一开始就拆了模块:
main.py——入口,负责初始化、主循环、事件转发。config.py——所有常量集中放,包括窗口尺寸、颜色、单位属性表、建造价格。map_grid.py——地图数据结构、碰撞判断、寻路。entities.py——单位类和建筑类,以及它们的渲染、移动、攻击逻辑。economy.py——资源、电力、人口的计算。ai_logic.py——敌方 AI 的状态机和决策逻辑。ui.py——侧边栏、建造菜单、选中信息面板。
这个拆分方式还有个额外好处:每次让 AI 干活时,我只需要把相关模块的文件内容贴给它,上下文不会爆炸。比如让它改ai_logic.py,我只需要给它看这个文件加上兵种配置表就够了,不用把渲染代码也塞进去。后来实践证明,控制好每次请求的上下文范围,是让 AI 输出高质量代码的关键手段之一。
3. 核心系统是怎么一步步搭起来的
3.1 地图与格子系统:一切的地基
RTS 的地图本质是一张二维数组。我们的地图是 64×64 个格子,每个格子的值代表地形类型:0 是平地,1 是矿,2 是障碍物(比如树和岩石)。渲染的时候按格子画色块:平地画成浅绿,矿画成黄色带小点,障碍物画成深绿色。
地图系统除了画格子,还有一个更重要的职责:碰撞和寻路。单位要移动的时候,不能直接直线走过去,得绕过障碍物。64×64 的网格不算大,所以我让 AI 实现了一个 BFS 寻路算法。BFS 虽然不如 A* 高级,但对这种规模的地图完全够用,而且代码极其直观,AI 不容易写错。
这里有个很妙的简化:我没让单位精确占据格子中心,而是让每个单位有一个浮点坐标,寻路时先把坐标换算到所在格子,然后用 BFS 算出经过哪些格子,再让单位沿着格子中心路径走。这样视觉上移动很平滑,但底层的碰撞和寻路全是离散的,省了大量计算。矿点和障碍物则单独维护一个集合,刷地图的时候提前生成好。
3.2 单位与建筑:数据驱动设计
单位系统是这个项目里我最坚持的一个设计决策:所有兵种和建筑都用数据表描述,逻辑代码不直接写死任何数值。
举个例子,兵种配置是这样一张 Python 字典表:
UNITS = { "soldier": {"hp": 100, "damage": 10, "range": 80, "speed": 120, "cooldown": 0.6, "cost": 200, "population": 1}, "rocketeer": {"hp": 80, "damage": 25, "range": 140, "speed": 100, "cooldown": 1.0, "cost": 400, "population": 1}, "tank": {"hp": 350, "damage": 40, "range": 120, "speed": 80, "cooldown": 1.2, "cost": 800, "population": 2}, }单位类本身只负责读表、移动、开火、扣血,不关心具体数值。这样做的好处非常多:AI 出 bug 时,往往只需要调数值就能修;玩家反馈“坦克太弱了”,我改一行配置就行;后续加新兵种也只是往表里加一项。
建筑也类似,分成power_plant(电厂)、barracks(兵营)、mine(矿场)、war_factory(坦克厂)、base(基地)。每种建筑有占地尺寸、造价、建造时间和功能回调。建筑占地是个矩形区域,建造时要检查这块地上的格子是否全部为空,否则不能放。这个检查逻辑我专门让 AI 写了一个单元测试,因为它是很多 bug 的来源。
3.3 战争迷雾与视野系统
RTS 游戏如果没有战争迷雾,战略深度会大打折扣。我们的实现并不复杂:每帧根据所有己方单位的视野半径,在visibility数组上画出可见区域。这个数组同样是 64×64,每个格子的值是 0(从未探索)、1(探索过但现在看不见)、2(当前可见)。
渲染时有个关键优化:不直接把整个地图画出来,而是只画玩家屏幕可见范围内的格子,并且不在视野内的单位一律不画。等视野外单位重新进入视野时,才把它画出来。早期版本我把所有单位都画了,结果对方基地的动向一目了然,完全没有迷雾效果。后来改成渲染前先判断单位是否在可见格子上,这才正常。
战争迷雾这个功能看着简单,但它涉及渲染和逻辑两个层面的配合。AI 最开始实现时只顾着画地图,忘了把单位从迷雾里藏起来,我试玩时发现了这个 bug,跟它描述了一遍“单位不在视野里就不能显示”,它很快就修好了。这种问题靠人眼验收非常快,比写测试还高效。
3.4 敌方 AI:状态机让你的对手动起来
敌方 AI 是整个项目里最容易被低估的部分。要让人感觉对面“像一个玩家在玩”,其实只需要一个非常简单的状态机,每个状态做固定几件事:
gather状态:AI 基地持续生产采矿车,采矿车自动去矿点采集,运回基地换成钱。build状态:当钱积累到一定程度,AI 交替造兵营和坦克厂,并开始出作战单位。attack状态:当 AI 的作战单位数量达到阈值(比如步兵 10 个或坦克 4 个),它会集合这些单位,向玩家基地方向发起一波进攻。
这个状态机每 0.5 秒跑一次决策逻辑,没必要每帧都跑。为了增加挑战性,我给 AI 加了一点“作弊系数”,它采矿的效率是玩家的 1.5 倍,建造时间也比玩家短一点。否则以 AI 的决策水平,基本打不过一个会玩的真人。
最关键在于攻击路径:AI 把单位送到玩家基地门口,单位到达后会攻击视野内的玩家单位或建筑。这里我让 AI 给每个单位加了一个简单目标选择规则——优先攻击最近的敌对单位,如果没有单位就攻击敌方基地。这套规则写起来很直接,但效果已经能骗过大部分人了,至少我朋友玩的时候真的产生了紧迫感。
3.5 资源与经济循环
经济系统是老 RTS 的灵魂,没有经济压力,玩家就会像玩沙盘一样乱造。我设计的循环非常简单:采矿车从矿点挖矿,运回矿场,矿场把矿转换成资金;电厂决定电力上限,兵营和坦克厂生产单位需要消耗人口,而人口上限由建筑数量决定。
具体来说,广场上有多个矿点,每个矿点储量有限,采完就枯竭,你需要去更远的矿点。矿车的行为是个典型的“去—采—回—卸”循环,状态机跟 AI 一样:travel_to_resource(去矿点)→harvest(采 2 秒)→travel_to_base(回矿场)→unload(卸货,加钱)。每个状态持续的时间用计时器控制。
这个循环出现的 bug 很有意思:矿车卸货时如果矿场正在被攻击导致血量过低,它可能不知道该去哪里卸货。修法也简单,在寻路函数里加了一个“找不到目标就回基地旁边发呆”的兜底逻辑。这类边界情况是 AI 不太容易自己想到的,必须靠试玩才能发现。
4. 实操实录:我如何用 GPT 6.1 把想法变成代码
4.1 第一批 Prompt:目标越小,产出越靠谱
如果一开始我就对模型说“请帮我写一个红警”,大概率会得到一堆稀碎且无法运行的代码。我的做法是先把项目拆成十几个可验证的小阶段,每阶段只提一个明确目标。
比如第一阶段的目标是:在 Pygame 窗口里画出 64×64 的格子地图,并且支持鼠标拖拽移动视角。我给模型的 Prompt 写得非常具体,包括窗口大小、格子像素、地形数组的生成方式,以及要求“移动视角用拖拽方式实现”。它一次就写出了可以运行的代码,我看一眼没问题,就进入下一阶段。
前后总共拆成了这些阶段:
- 画一个 64×64 的格子地图,支持拖拽视角。
- 在格子上生成矿点和障碍物。
- 定义单位类,支持选择一个单位并显示属性面板。
- 实现右键移动,单位沿 BFS 路径行走。
- 实现选择框框选多个单位。
- 实现建造菜单,点击建造建筑。
- 实现电厂、矿场、兵营、坦克厂的功能。
- 实现采矿车自动采集和卸货。
- 实现生产列表和人口限制。
- 实现敌方 AI 的发展、出兵、进攻。
- 实现单位攻击、血条、死亡消失。
- 实现战争迷雾。
- 实现胜负判定和重启游戏。
每个阶段基本在半小时到一小时之间完成。阶段越小,模型犯错的概率越低,我验收的成本也越低。这个节奏到了后半段尤其重要,因为代码量上去之后,一个大改动往往牵一发动全身。
4.2 迭代式对话:不是写代码,是当监工
真实的开发过程不像很多人想象的“问一句就完事”,而是几十轮的持续对话。我经常在 AI 给的代码跑不起来时,直接把报错信息完整贴给它,然后加一句:“请说明你做了哪些修改来修复这个问题。”
这里有一个很关键的技巧:让模型不仅给代码,还要解释为什么。比如它修改了某个函数的参数顺序,如果不说明原因,我合入代码时根本不知道这个改动是否引入了新问题。后来我要求所有关键修改都附带一两句解释,代码评审效率立刻提升。
迭代过程中最常见的对话模式是这样的:
- 我:现在点击兵营时没有弹出建造菜单,检查一下 UI 层的事件绑定。
- 它:问题可能出在
ui.py的handle_click方法,事件坐标没有从屏幕坐标转换成世界坐标,导致点击位置和建筑位置对不上。我修改了转换逻辑。 - 我:好的,重新运行后菜单出现了,但点建造按钮没反应。
- 它:那应该是建造按钮的矩形区域计算错误,我打印了一下坐标,发现按钮尺寸被设置成 0,修正后测试通过。
这种有来有回的调试过程占了整个开发时间的一半还多。AI 不像搜索引擎那样给你一堆链接,它更像一个坐在你旁边的同事,你说问题,它给方案,然后你去验证,再回来告诉它结果。
4.3 让 AI 代码“听话”的三个技巧
经过这次项目,我总结了三个让大模型输出质量直线上升的小技巧,跟它本身的版本迭代无关,更多是沟通层面的经验。
第一个技巧是给例子而不是下定义。比如想让 AI 明白“建筑占多个格子”,不要说“请实现占地面积逻辑”,而是给出具体例子:“基地占 4×4 格,建造时这些格子必须全部是空地,每个格子坐标都要做碰撞检测”。模型的抽象能力强,但具体例子能大幅减少歧义。
第二个技巧是要求它“写完立刻自测”。每段代码交付前,让它自己检查是否存在明显的越界、空指针、逻辑死循环,如果发现问题先修好再交给我。有些模型会偷懒,只在 Prompt 明确要求时才带着测试跑一遍。我们的map_grid.py里就有它加的一段检查代码,用来确保路径不会穿过障碍物。
第三个技巧是控制代码块的颗粒度。遇到大功能时,明确要求它“先给出这个模块的接口签名、全局变量列表,以及各函数之间如何调用,不要写实现”。等我把整体设计看过、确认没问题,再让它分步填充实现。这相当于没让 AI 跳过设计阶段,直接手写复杂系统。事实证明,跳过设计直接生成的代码,往往在模块边界上会出问题。
4.4 哪些坑是 AI 填不了、只能自己动手的
虽然大部分代码是 AI 写的,但有两类问题它很难独立解决,只能靠人工介入。
一类是全局性性能问题。比如游戏后期单位一多帧率暴跌,AI 给出的优化建议常常是零碎的——减少画布刷新率、优化单位数量上限,但它看不到整体瓶颈在哪。我后来自己排查,发现瓶颈主要是两个:一是渲染时用了太多pygame.draw.circle绘制血条和选中框,改成预渲染图片后明显变快;二是 BFS 在每帧对每个单位都重新计算,改成只有当起点或终点变化时才重算,性能立刻改善。这类全局判断,AI 没有完整上下文,我做起来反而更快。
另一类是数值手感问题。游戏好不好玩,不在于代码对不对,而在于数值调得顺不顺。AI 给出的数值往往很保守——步兵血量 100、坦克伤害 40,听起来合理,但实际玩起来要么太脆要么太肉。这个必须反复试玩,一点一点调:步兵要能扛住矿车探路时的零散火力,坦克对步兵的压制力要明显,矿车采集效率要刚好让经济有节奏感。这些经验完全不在 AI 的训练数据里,它给不了太多帮助。
所以最后的复盘结论是:AI 写了八成代码,但真正让它变成“一个能玩的游戏”的,恰恰是我自己调的那两成。
5. 开源之后:仓库维护与社区反馈
5.1 许可证和 README 是开源的第一步
代码写得再漂亮,如果 README 不行,别人拿到手也是一脸懵。我的 README 开头放了一句话简介和一张 GIF 动图。动图是跑起来之后用录屏软件录的,花了半分钟。别小看这张图,很多访客就是靠它判断“这玩意到底能不能玩”。
许可证选的是 MIT。理由很简单:我希望别人下载之后随便改、随便用,不需要顾虑版权问题。RTS 游戏的代码结构本身不复杂,开源出去不会损害任何商业价值,反而可能吸引别人来帮忙补功能。实际上开源之后真的有人提 PR,虽然只是加音效和修了两个操作 bug,但那种“陌生人在帮你改代码”的感觉还是很爽的。
5.2 收到 issue 和 PR 之后我怎么处理
开源之后收到的问题大概可以分成三类。第一类是环境问题,比如有人用的 Python 版本太高导致 Pygame 装不上,或者在中国网络环境下镜像源有问题,这种一般回复里给一段安装命令就能解决。第二类是功能建议,最多的是“希望能保存游戏进度”,我暂时没做,就在 issue 里标了feature-request,让有兴趣的人自己提 PR。第三类是真 bug,比如有人发现快速双击建造按钮会重复扣钱,这个我复现之后两分钟就修了。
处理 PR 的原则是“小改动直接合,大改动先讨论”。加音效这种跟核心逻辑完全不冲突的,直接合并;如果有人改 AI 状态的,我会先在本地跑几局,确认不会把游戏搞失衡再合。开源不是把代码扔上去就完事,而是持续维护一个小项目的运转节奏。
5.3 常见问题速查表
| 问题 | 原因 | 解决办法 |
|---|---|---|
| 游戏后期单位一多就卡顿 | 每帧重复计算 BFS、用绘图函数频繁画圆 | 路径缓存、用预渲染贴图替代部分绘制 |
| AI 不进攻,一直缩在家里 | 状态机里attack的触发条件太严格 | 调低出兵数量阈值或缩短集结等待时间 |
| 建造建筑位置不对 | 鼠标坐标没从屏幕坐标转世界坐标 | 加上相机偏移量的换算 |
| 单位卡在障碍物里 | BFS 路径和单位碰撞半径不一致 | 单位移动时加一步碰撞检测,超出就回退上一格 |
| 中文乱码 | Pygame 默认字体不支持中文 | 加载系统字体或改用英文界面 |
这份表格是真实踩坑的记录,也是开源社区里大家问得最多的几个点。每次有人问类似问题,我把这个表格链接发过去就行,省事了不少。
6. 写在最后的真心话
这次项目给我最大的感受,不是“AI 能写游戏了”这种口号式的震撼,而是“当你能把需求拆得足够细,AI 产出的质量会出乎意料地高”。过去我们写代码,最大的成本往往不是敲键盘,而是想清楚边界、想清楚模块怎么划分、想清楚怎么验证——这些能力不因工具变化而贬值。
如果你也想试试类似的事情,我的建议很直接:别一上来就做红警,先从 2048 或者扫雷开始,把 AI 协作的上手节奏跑通。等你能熟练地用小步迭代驾驭它了,再挑战更复杂的游戏类型也不迟。
做完这个项目之后,我自己对 AI 编程的心态也变了。以前总觉得模型输出质量决定一切,现在觉得真正决定天花板的,是你能不能把一个大而模糊的想法,翻译成一连串小而具体的任务。这个能力,在任何时代都不会过时。