BREAKOUT:磷光纪元——把打砖块做完整
本文完全由 WorkBuddy 独立收集资料、调试代码并完成文章写作。
我是 Alex Xiang,前百度/微博工程师,现在专注于 AI 工程与工具产品。更多文章欢迎关注微信公众号「字与码」。
打砖块可能是最没有秘密的游戏类型:一块挡板,一颗球,一整面等着被拆开的砖。规则一句话就能说完,所以它最容易被做浅——加粒子、加弹幕、加数值,就是不肯把“球撞上砖那一刻”做准。
这两天我把这个东西做完整了:四十八个手工关卡、六个由砖块构成的 Boss、一套局内构筑,还有两个平台的可运行版本。这篇文章讲它是什么、怎么做的,以及哪些地方我还没做好。
先玩为敬
免安装,桌面浏览器直接打开:
https://thunder-vector.app.workbuddy.host/breakout.html?sharecode=PALSNCSU6Z
鼠标移动控板,空格或左键发球,ESC 暂停。进度存在浏览器本地,关掉再开还在。
需要说明的是:手机浏览器只做了横屏提示和触屏拖动,没有做过真机验收,请用桌面浏览器玩。

主菜单没有做多余的动效,只有一层缓慢漂移的砖块剪影。扫描线、屏幕抖动、粒子密度都可以在设置里关掉——这三个是最容易让人误以为“复古就是糊一层滤镜”的东西,所以必须能关。
四十八个关卡,不是四十八次重复
六章,每章八关。每一关在数据里都记着三件事:这一关教什么、主要风险是什么、清关路线大概长什么样。第一章从“冷启动”的整齐砖墙开始,教接球和边缘角度;到“窄门”时,目标被立柱切成几块,逼迫你穿窗口精确击打。
砖块有六类:普通、双层、装甲、爆裂、再生、不可破坏障碍。它们不靠颜色区分——装甲砖有斜向纹和四角铆钉,双层砖是横向双条、剩余耐久决定亮几条,爆裂砖中间是菱形核心。颜色只是辅助。

关卡数据是字符图,十二列,一行一个字符串。写关卡的时候不碰代码,只改 content.js。同时有一个可通关性校验:把不可破坏砖当墙做一次连通性搜索,任何四面被彻底封死的可破坏砖都会被标记成“不可通关风险”。这条校验在开发中真的抓到过一关——顶部整行是障碍,下面一整块砖根本打不到。
六个 Boss,都不是血更厚的一排砖
打砖块的 Boss 很容易做成“一排砖,血量乘十”。我给自己定的规则是:Boss 必须改变你的反弹路线,而不是延长你的劳动时间。
“终端守卫”有四块护甲绕着核心转,你得在它们转开的间隙打进去;“冰镜双核”是两个核心轮流被镜面罩住,破镜之后只有六秒窗口;“折跃巨蛇”有九节身体沿正弦路径游动,只有头部是弱点,打身体它会慢慢再生。

共同点是三件事:护甲没破时核心明确显示为无敌(会有提示,不会让你白打);每次阶段转换有文字预告;弱点窗口有倒计时。不做无法反应的弹幕——这始终是个打砖块游戏。

这一张是冰镜双核。金色框是镜面,中间橙色带脉冲的是核心。核心最后绘制,否则会被镜面完全盖住——这是我截第一版图时才发现的问题。
远征:把一局变成一条路线
经典模式是四十八关顺序解锁,固定三条命,没有成长。远征模式换了骨架:随机抽三章,每章五层,每层两个选项——战斗、精英、商店、事件、补给,最后是章节 Boss。

每次升级三选一,三十六项升级分六个方向:动能暴击(精准接球、贯穿、暴击)、烈焰爆破(灼烧、爆炸、扩散)、冰霜控制(冻结、碎裂、护盾)、雷电传导(导电、跳跃、连锁)、分裂多球、工程辅助(激光、无人机、磁吸)。另有十八件遗物、六种挡板、十二组跨系统协同。

设计上刻意避开“伤害 +5%”这类升级。每个升级都要改一件事:命中后附加状态、改变球的行为、改变砖阵交互、或者给接球加一个奖励条件。比如“精准校准”是挡板中心 18% 区域接球后,下一次碰撞必定暴击——它逼你去用挡板中间接球,而不是把球随便弹回去。
同时有一条硬约束:辅助不能替代控球。收尾辅助“残响引导”只在剩余三块砖、或者长时间没打中砖时生效,而且只在球位于上半场时才轻微转向;接近挡板时完全不干预。它帮你结束拖沓,不帮你打球。
手感是算出来的
球速最高 900 像素每秒,一帧能走十几个像素,比很多砖块还厚。用引擎自带的物理回调,高速下必然穿砖。所以碰撞是自己写的:
- 固定 120Hz 步长,与渲染帧率解耦,同一局面在任何机器上结果一致;
- 圆对矩形用“膨胀矩形 + 线段 slab”求最早接触时刻,一步之内的位移再大也不会跳过砖块;
- 同一时刻多重接触有明确优先级:挡板 > 砖块 > 墙,同类按时间取最早,一步最多解析八次接触;
- 每次反弹后强制保留最小垂直分量,避免球卡在近水平方向来回蹭。

测试里有一条是 6000 像素每秒的球打向单块砖,必须命中、必须弹回、坐标必须有限。这条断言在最后救了我一次。
一个把球打飞的 bug
开发中出过一次很典型的错。
给“方向”做角度限幅的函数返回的是单位向量,而调用处直接把这个返回值赋给了速度。结果第一次撞墙之后,球速从 520 掉到 1 像素每秒,然后慢悠悠地穿过砖块、穿过墙,飞出了场地。
第一次截图调试时只看到球在场地外,速度是 (1.0, -0.1),一开始还以为是碰撞没检测到。真正的原因是速度被自己归一化了——碰撞其实一直在发生,只是球太快变成“几乎不动”。
修法是把限幅函数改成保持输入向量的模长,并且在测试里加了一条断言:反弹前后速度误差必须小于 1 像素每秒。同一类错误后来在“近水平修正”里又出现过一次,原因是那里用了 dir()——它本来就是单位向量。
这件事之后我给自己定了个规矩:只要“方向”和“速度”出现在同一行代码里,就必须有断言。
为什么有两个版本
最早是 Godot 4.7.1 做的 Windows 版,导出了 exe。后来需求变成“要能网页运行”,首选当然是 Godot 的 Web 导出——一份代码两个平台。
但本机没有 4.7.1 的 Web 导出模板。官方模板包 1.28GB,实测下载速度 27KB/s;想用 HTTP Range 只取其中 web 那一份,代理会在十秒内切断连接。这条路线在当前网络下不可行。
所以网页版是另写的:原生 HTML5 + Canvas 2D,零依赖,源码一百多 KB,首屏不用下 wasm。代价是两套实现——规则、常量、关卡数据共用一份,测试也跑两套(网页版 73 项核心 + 515 项完整内容,Godot 侧 81 项)。
这个决定我不觉得漂亮,但它是当时唯一能同时交付“能玩的网页版”和“能跑的 exe”的路。将来网络允许,还是应该回到一份代码。
现在还没做好的
- 没有真人试玩。Boss 要打多久、哪条构筑过强、商店定价是否合理,目前只有自动驾驶和规则测试的数据,不能当作手感结论。
- Windows 版停在早期阶段。exe 里只有五关,没有 Boss 和构筑;完整内容目前只在网页版。
- 移动端没验收。只有横屏提示和触屏拖动。
- 无尽模式维度单一。现在主要靠球速和血量加压,规则变化还不够。
我也不打算写“可以玩十小时”这种话——没有试玩数据支撑的时长,写出来就是假的。
试玩
再放一次地址:
https://thunder-vector.app.workbuddy.host/breakout.html?sharecode=PALSNCSU6Z
如果玩下来觉得某一关特别烦、某个 Boss 的窗口太短、或者某条构筑明显比别的强,欢迎直接告诉我。这类反馈比“做得不错”有用得多。
写在最后
打砖块好玩的地方从来不是“更多”,而是“可控”:你知道球会往哪弹,你知道差一点是因为自己没接准,而不是因为系统随机。四十八关、六个 Boss、三十六项升级,都是在这条前提上长出来的。
一旦这个前提被破坏——球速失控、反弹不可预期、辅助替你打球——内容再多也没用。
这也是我做这个项目最想验证的一件事。
微信公众号
欢迎关注「字与码」
如果这篇文章对你有用,也欢迎在微信里继续关注后续更新。
X / Twitter
关注 @ax2_zicode
更即时的技术观察、新文章提醒和一些短想法会发在 X 上。