我们曾用 AirPods 做过一只“头部鼠标”:转头,光标移动;回正,光标停止。
它证明耳机可以成为输入设备,也留下一个更有趣的问题:
这种输入方式,能不能变成一款真正好玩的游戏?
于是我们做了《颠了个球》。
玩家点头启程,左右转头控制球拍。坚持越久,飞得越远:从地面穿过云层,遇见热气球、莱特飞行者、东方红一号、空间站和月球,再飞过太阳系,进入有彗星、超新星与黑洞的深空。
宏大的旅程,并不是一次生成的。它最早只是一块板和一颗不断震动、穿板、失控的小球。
一、先看这次 VibeCoding 的规模
统计范围从第一条 EXP-005 需求,到 v1.0.0 封板完成:
左右滑动查看完整表格
| 项目 | 结果 |
|---|---|
| 可识别并保留记录的版本 | 36 个 |
| 从立项到封板的自然时间 | 4 天 8 小时 19 分 |
| Codex 任务遥测处理量 | 约 3.47 亿 Token |
| 其中长上下文缓存命中 | 约 3.32 亿 Token |
| 扣除缓存后的新增输入与输出 | 约 1471 万 Token |
“自然时间”不是连续工作时长;Token 也不是文章字数或简单的计费量。这个数字包含 AI 在长任务中反复读取上下文、代码、日志和工具结果的处理量,所以同时列出缓存口径,避免误读。
真正值得关注的不是数字有多大,而是这 36 个版本没有变成 36 次盲目改动。每一轮都遵循同一个循环:
- 提出一个可观察目标
- AI 实现并启动
- 真实佩戴与试玩
- 记录最具体的不舒服
↻ 反馈进入下一轮
我们把它叫作:
目标 → AI 实现 → 灰测
二、第一版:功能很多,但根本不好玩
最初的设计很“完整”:转头控制左右,抬头低头控制上下,歪头改变球拍角度。三个动作同时追球、接球和调整反射方向。
戴上耳机后,我们立刻发现:人的头不是三个互不干扰的摇杆。控制越多,身体负担越大,游戏越像校准考试。
更糟的是,小球落在球拍上后开始高频震动。物理引擎已经计算了一次碰撞,程序又强制把球向上推,两套规则互相争夺小球。后来还出现了小球一帧穿过球拍、计数瞬间增加的错误。
我们最终做了两次关键减法:
- 球拍先只保留左右移动。
- 不再用一堆物理参数描述“真实”,而用“弹多高、多久落回”描述玩家节奏。
程序根据目标高度和起落时间计算球的运动;碰撞也不只相信某一帧的接触消息,而是检查小球上一帧到这一帧是否真正从上方穿过球拍。
这让我们得到第一条经验:
先定义玩家应该感受到的节奏,再选择物理实现。参数越多,不代表体验越真实。
三、三种控制模型,只有一种像“拖动”
球稳定以后,问题变成:怎样把转头变成球拍移动?
左右滑动查看完整表格
| 模型 | 规则 | 感受 |
|---|---|---|
| 绝对位置 | 头转到哪里,球拍就到哪里 | 回正时球拍也回中央,像拉橡皮筋 |
| 按键式 | 超过阈值后固定速度左移或右移 | 容易理解,但难以微调 |
| 连续速率 | 转头幅度决定速度,回正后停在原处 | 像用鼠标拖动,能微调也能追球 |
我们最后选择连续速率:轻微转头慢移,大幅转头快移,回正停止。头部自然晃动落在“停止区”内,不让球拍漂移。
点头后来重新加入,但不再承担大范围位置控制。它像听歌时打拍子:玩家做一个明显动作,球拍只轻轻震动,同时为下一次击球积蓄一点力量。
体感游戏的核心不是“识别了多少动作”,而是:
一个动作,最好只表达一个清楚意图。
四、先定义“颠球一次”,再写计数
最初,我们把“球拍收到接触消息”理解为颠球一次。可一次碰撞可能产生多条消息,旧消息也可能延迟到达,于是计数失真。
后来,我们先用普通语言定义事件:
小球正在向下运动,并真正从球拍上方穿过可接区域;程序执行一次向上反弹,才算颠球一次。
这样便能排除:
- 从球拍下方碰到;
- 同一段持续接触;
- 延迟到达的旧消息;
- 修复轻微穿透时产生的重复计数。
这条经验不只适用于游戏:
先定义“对用户而言发生了什么”,再寻找代码回调。
五、从竞技场到一段上升旅程
基础玩法稳定后,我们开始问:玩家为什么愿意一直颠下去?
第一版答案是一座“云端竞技场”。
它比纯色原型漂亮,却没有时间感。我们随后把“坚持多久”解释成“飞了多远”:玩家从地球发射场起飞,穿过航空史、地球轨道、月球和太阳系,最后进入深空。
新方向第一次实现时仍然很假:地面像从火箭上俯视地球;上下两块天空颜色断裂;云从画面中淡入;飞行器同速横移,有的甚至倒着飞;月球和行星突然出现又消失。
我们因此建立了一套统一的空间语言:
- 从人类平视的发射场出发,地面向下完整离屏;
- 起飞一段时间后才逐渐接近云层;
- 天空由蓝连续过渡到黑,不拼接两张背景;
- 飞行器和天体不淡出,而是完整飞出画面;
- 慢速对象停留更久,高速对象从远处小尺度接近,再快速掠过;
- 航空器按典型飞行高度出现,而不是机械地按年份排队;
- 行星留出观察时间,不像道具一样从天上掉下来。
这套风格后来被概括为:
轻科幻航空航天纪录片。
它让界面、动画、声音和文案有了共同判断标准。
六、生成图片,只是资产工作的开始
图片生成很快,但直接放进游戏后,我们遇到了各种问题:
- 热气球只有气囊,没有吊篮;
- 一张图混入相邻飞行器的机翼;
- 飞机朝向与运动方向相反;
- 图片带白底、色键残边或水印;
- 单帧漂亮,移动起来却像 PPT;
- 画的是登月舱,剧情需要的却是返回地球的指令舱。
于是,图片必须经过完整管线:
确认剧情身份
→ 寻找来源或生成原稿
→ 清理背景与边缘
→ 检查主体、朝向和尺度
→ 放入真实运动场景
→ 记录来源、权利与替换状态
土星五号、鹰号和哥伦比亚后来采用来源明确的三维模型;稳定的球、球拍和碰撞仍保持二维。三维只进入少数历史遭遇,既增加空间感,也不动摇核心玩法。
彗星、流星雨、白矮星和超新星则采用“稳定核心 + 动态物质层”:尾迹、尘埃、喷流和冲击波分别运动,不再拖着一张图片横穿屏幕。
由此得到的结论是:
生成图片解决原稿问题;身份、完整性、运动、来源和权利,才决定它是不是游戏资产。
七、让真实反馈推动版本,而不是让功能表推动
项目中最有价值的需求,往往只有一句话:
左右滑动查看完整表格
| 真实反馈 | 下一轮改变 |
|---|---|
| “球太快了。” | 用弹高和周期重新定义球感 |
| “小球穿过了球拍。” | 增加连续路径碰撞 |
| “移动不像鼠标拖动。” | 改用连续速率控制 |
| “摇头太大直接回首页。” | 游戏中关闭返回手势 |
| “两种蓝色太割裂。” | 改为单一连续天空 |
| “热气球像贴纸。” | 加入独立速度、弧线与轻摆 |
| “彗星像 PPT,流星像蝌蚪。” | 改为动态尾迹、岩屑和粒子 |
| “点头启动不灵。” | 调整动作时限与回正范围 |
AI 能迅速改代码、补测试、重建并启动新版本;人负责它无法代替的部分:戴上耳机、感受疲劳、判断球感、识别“太假”,以及决定下一步。
所以 VibeCoding 的分工不是“人提需求,AI 全自动完成”,而是:
AI 负责快速实现和系统验证,人负责真实世界、审美与方向。
八、作弊工具应该留在开发版
正常玩到月球和深空需要很久。为了测试后半程,我们增加了旅程加速、超宽球拍、免死模式、章节预览、声音试听和场景快照。
这些“作弊”让测试高效,却不应该出现在玩家界面。
封板时,我们保留完整开发构建,同时生成独立正式版:隐藏灰测入口,恢复标准速度、球拍宽度和旅程节奏。
封板前,我们还完成了四层验证:
自动测试
→ 真实窗口
→ 真实 AirPods 佩戴
→ 压缩、解压并从新目录启动
它们不能互相替代。程序测试通过,不代表头戴起来舒服;本机能运行,也不代表同事电脑能正常打开;内部测试包能解压,更不等于已经完成公开发行所需的签名、公证和素材权利复核。
九、这次项目留下的七条经验
- 第一版先减自由度。 先让一个动作好用,再增加第二个动作。
- 先定节奏,再定物理。 玩家感受到的是高度与时间,不是参数数量。
- 先定义事件语义。 接触、反弹和计分不是同一件事。
- 尽早建立开发旁路。 长时间线必须有加速、预览和免死。
- 从第一张图开始管理资产。 记录身份、来源、权利、朝向和运行状态。
- 实体不用淡出逃离现场。 飞行器与天体应通过位置和尺度离场。
- 满意后要敢于封板。 不再增加内容,只修阻断体验的明确缺陷。
十、零基础用户怎样开始
不要从整段太阳系旅程开始。四步就够:
- 让 AirPods 运动输入控制一个左右移动的方块。
- 加入一块球拍和一颗按固定节奏起落的小球。
- 真实比较绝对位置、按键式和连续速率控制。
- 核心玩法稳定后,再加入短背景旅程和第一件资产。
第一轮可以直接这样告诉 AI:
我想制作一个运行在 macOS 上的 AirPods 头部控制小游戏。
本轮只做最小可玩版本:
1. 使用 Apple 公开接口读取兼容 AirPods 的头部运动。
2. 左右转头控制水平球拍,回正后停在当前位置。
3. 小球按稳定、可预测的节奏下落和反弹。
4. 小球从上方真正撞到球拍才反弹并计数,漏接后结束。
5. 暂不加入上下控制、角度、道具、背景故事和三维资产。
6. 分别记录自动测试、窗口检查和真实 AirPods 灰测。
请先提出可观察的验收标准,再开始实现。
结语
《颠了个球》最后留下了一款游戏,也留下了一条更重要的经验:
AI 让版本快速出现,真实试玩决定版本是否值得留下。
当我们不断追问“动作自然吗、球有节奏吗、画面像真的在飞吗、还愿意再玩一局吗”,一副耳机才真正从传感器设备,变成了游戏控制器。
