为 AirPods 做一个游戏

第一版功能很多,却根本不好玩。直到我把“颠球一次”说清楚,让动作、物理和旅程围绕同一个体验收敛。

两只白色无线耳机之间升起小型飞行器,并沿轨道飞向行星

我们曾用 AirPods 做过一只“头部鼠标”:转头,光标移动;回正,光标停止。

它证明耳机可以成为输入设备,也留下一个更有趣的问题:

这种输入方式,能不能变成一款真正好玩的游戏?

于是我们做了《颠了个球》。

玩家点头启程,左右转头控制球拍。坚持越久,飞得越远:从地面穿过云层,遇见热气球、莱特飞行者、东方红一号、空间站和月球,再飞过太阳系,进入有彗星、超新星与黑洞的深空。

宏大的旅程,并不是一次生成的。它最早只是一块板和一颗不断震动、穿板、失控的小球。

一、先看这次 VibeCoding 的规模

统计范围从第一条 EXP-005 需求,到 v1.0.0 封板完成:

左右滑动查看完整表格

项目 结果
可识别并保留记录的版本 36
从立项到封板的自然时间 4 天 8 小时 19 分
Codex 任务遥测处理量 3.47 亿 Token
其中长上下文缓存命中 3.32 亿 Token
扣除缓存后的新增输入与输出 1471 万 Token

“自然时间”不是连续工作时长;Token 也不是文章字数或简单的计费量。这个数字包含 AI 在长任务中反复读取上下文、代码、日志和工具结果的处理量,所以同时列出缓存口径,避免误读。

真正值得关注的不是数字有多大,而是这 36 个版本没有变成 36 次盲目改动。每一轮都遵循同一个循环:

流程概览
  1. 提出一个可观察目标
  2. AI 实现并启动
  3. 真实佩戴与试玩
  4. 记录最具体的不舒服

↻ 反馈进入下一轮

我们把它叫作:

目标 → AI 实现 → 灰测

二、第一版:功能很多,但根本不好玩

最初的设计很“完整”:转头控制左右,抬头低头控制上下,歪头改变球拍角度。三个动作同时追球、接球和调整反射方向。

戴上耳机后,我们立刻发现:人的头不是三个互不干扰的摇杆。控制越多,身体负担越大,游戏越像校准考试。

更糟的是,小球落在球拍上后开始高频震动。物理引擎已经计算了一次碰撞,程序又强制把球向上推,两套规则互相争夺小球。后来还出现了小球一帧穿过球拍、计数瞬间增加的错误。

颠了个球 v0.1.1 的浅蓝色早期界面,中央只有小球、托盘和佩戴耳机提示
v0.1.1:先验证最基础的小球、托盘和 AirPods 头部控制。

我们最终做了两次关键减法:

  1. 球拍先只保留左右移动。
  2. 不再用一堆物理参数描述“真实”,而用“弹多高、多久落回”描述玩家节奏。

程序根据目标高度和起落时间计算球的运动;碰撞也不只相信某一帧的接触消息,而是检查小球上一帧到这一帧是否真正从上方穿过球拍。

这让我们得到第一条经验:

先定义玩家应该感受到的节奏,再选择物理实现。参数越多,不代表体验越真实。

三、三种控制模型,只有一种像“拖动”

球稳定以后,问题变成:怎样把转头变成球拍移动?

左右滑动查看完整表格

模型 规则 感受
绝对位置 头转到哪里,球拍就到哪里 回正时球拍也回中央,像拉橡皮筋
按键式 超过阈值后固定速度左移或右移 容易理解,但难以微调
连续速率 转头幅度决定速度,回正后停在原处 像用鼠标拖动,能微调也能追球

我们最后选择连续速率:轻微转头慢移,大幅转头快移,回正停止。头部自然晃动落在“停止区”内,不让球拍漂移。

点头后来重新加入,但不再承担大范围位置控制。它像听歌时打拍子:玩家做一个明显动作,球拍只轻轻震动,同时为下一次击球积蓄一点力量。

体感游戏的核心不是“识别了多少动作”,而是:

一个动作,最好只表达一个清楚意图。

四、先定义“颠球一次”,再写计数

最初,我们把“球拍收到接触消息”理解为颠球一次。可一次碰撞可能产生多条消息,旧消息也可能延迟到达,于是计数失真。

后来,我们先用普通语言定义事件:

小球正在向下运动,并真正从球拍上方穿过可接区域;程序执行一次向上反弹,才算颠球一次。

这样便能排除:

  • 从球拍下方碰到;
  • 同一段持续接触;
  • 延迟到达的旧消息;
  • 修复轻微穿透时产生的重复计数。

这条经验不只适用于游戏:

先定义“对用户而言发生了什么”,再寻找代码回调。

五、从竞技场到一段上升旅程

基础玩法稳定后,我们开始问:玩家为什么愿意一直颠下去?

第一版答案是一座“云端竞技场”。

颠了个球 v0.8.0 的云端竞技场,天空和竞技场背景围绕中央托盘展开
v0.8.0:物理原型开始拥有云端竞技场和更完整的视觉反馈。

它比纯色原型漂亮,却没有时间感。我们随后把“坚持多久”解释成“飞了多远”:玩家从地球发射场起飞,穿过航空史、地球轨道、月球和太阳系,最后进入深空。

新方向第一次实现时仍然很假:地面像从火箭上俯视地球;上下两块天空颜色断裂;云从画面中淡入;飞行器同速横移,有的甚至倒着飞;月球和行星突然出现又消失。

颠了个球 v0.13.5 的蓝色天空场景,画面中央能看到明显天空接缝和本局结束面板
v0.13.5:画面升级也暴露了天空接缝和界面整合问题。

我们因此建立了一套统一的空间语言:

  • 从人类平视的发射场出发,地面向下完整离屏;
  • 起飞一段时间后才逐渐接近云层;
  • 天空由蓝连续过渡到黑,不拼接两张背景;
  • 飞行器和天体不淡出,而是完整飞出画面;
  • 慢速对象停留更久,高速对象从远处小尺度接近,再快速掠过;
  • 航空器按典型飞行高度出现,而不是机械地按年份排队;
  • 行星留出观察时间,不像道具一样从天上掉下来。

这套风格后来被概括为:

轻科幻航空航天纪录片。

它让界面、动画、声音和文案有了共同判断标准。

六、生成图片,只是资产工作的开始

图片生成很快,但直接放进游戏后,我们遇到了各种问题:

  • 热气球只有气囊,没有吊篮;
  • 一张图混入相邻飞行器的机翼;
  • 飞机朝向与运动方向相反;
  • 图片带白底、色键残边或水印;
  • 单帧漂亮,移动起来却像 PPT;
  • 画的是登月舱,剧情需要的却是返回地球的指令舱。

于是,图片必须经过完整管线:

确认剧情身份
  → 寻找来源或生成原稿
  → 清理背景与边缘
  → 检查主体、朝向和尺度
  → 放入真实运动场景
  → 记录来源、权利与替换状态

土星五号、鹰号和哥伦比亚后来采用来源明确的三维模型;稳定的球、球拍和碰撞仍保持二维。三维只进入少数历史遭遇,既增加空间感,也不动摇核心玩法。

彗星、流星雨、白矮星和超新星则采用“稳定核心 + 动态物质层”:尾迹、尘埃、喷流和冲击波分别运动,不再拖着一张图片横穿屏幕。

颠了个球 v0.21.1 的深空场景,托盘前方出现明亮的程序化超新星
v0.21.1:游戏从地表继续前进到程序化生成的超新星场景。

由此得到的结论是:

生成图片解决原稿问题;身份、完整性、运动、来源和权利,才决定它是不是游戏资产。

七、让真实反馈推动版本,而不是让功能表推动

项目中最有价值的需求,往往只有一句话:

左右滑动查看完整表格

真实反馈 下一轮改变
“球太快了。” 用弹高和周期重新定义球感
“小球穿过了球拍。” 增加连续路径碰撞
“移动不像鼠标拖动。” 改用连续速率控制
“摇头太大直接回首页。” 游戏中关闭返回手势
“两种蓝色太割裂。” 改为单一连续天空
“热气球像贴纸。” 加入独立速度、弧线与轻摆
“彗星像 PPT,流星像蝌蚪。” 改为动态尾迹、岩屑和粒子
“点头启动不灵。” 调整动作时限与回正范围

AI 能迅速改代码、补测试、重建并启动新版本;人负责它无法代替的部分:戴上耳机、感受疲劳、判断球感、识别“太假”,以及决定下一步。

所以 VibeCoding 的分工不是“人提需求,AI 全自动完成”,而是:

AI 负责快速实现和系统验证,人负责真实世界、审美与方向。

八、作弊工具应该留在开发版

正常玩到月球和深空需要很久。为了测试后半程,我们增加了旅程加速、超宽球拍、免死模式、章节预览、声音试听和场景快照。

这些“作弊”让测试高效,却不应该出现在玩家界面。

封板时,我们保留完整开发构建,同时生成独立正式版:隐藏灰测入口,恢复标准速度、球拍宽度和旅程节奏。

颠了个球 v1.0.0 的正式版场景,荒野地表、远处塔架、托盘和佩戴耳机提示共同构成首屏
v1.0.0:最终版本把地表旅程、AirPods 输入提示和完整视觉语言收束到一起。

封板前,我们还完成了四层验证:

自动测试
  → 真实窗口
  → 真实 AirPods 佩戴
  → 压缩、解压并从新目录启动

它们不能互相替代。程序测试通过,不代表头戴起来舒服;本机能运行,也不代表同事电脑能正常打开;内部测试包能解压,更不等于已经完成公开发行所需的签名、公证和素材权利复核。

九、这次项目留下的七条经验

  1. 第一版先减自由度。 先让一个动作好用,再增加第二个动作。
  2. 先定节奏,再定物理。 玩家感受到的是高度与时间,不是参数数量。
  3. 先定义事件语义。 接触、反弹和计分不是同一件事。
  4. 尽早建立开发旁路。 长时间线必须有加速、预览和免死。
  5. 从第一张图开始管理资产。 记录身份、来源、权利、朝向和运行状态。
  6. 实体不用淡出逃离现场。 飞行器与天体应通过位置和尺度离场。
  7. 满意后要敢于封板。 不再增加内容,只修阻断体验的明确缺陷。

十、零基础用户怎样开始

不要从整段太阳系旅程开始。四步就够:

  1. 让 AirPods 运动输入控制一个左右移动的方块。
  2. 加入一块球拍和一颗按固定节奏起落的小球。
  3. 真实比较绝对位置、按键式和连续速率控制。
  4. 核心玩法稳定后,再加入短背景旅程和第一件资产。

第一轮可以直接这样告诉 AI:

我想制作一个运行在 macOS 上的 AirPods 头部控制小游戏。

本轮只做最小可玩版本:
1. 使用 Apple 公开接口读取兼容 AirPods 的头部运动。
2. 左右转头控制水平球拍,回正后停在当前位置。
3. 小球按稳定、可预测的节奏下落和反弹。
4. 小球从上方真正撞到球拍才反弹并计数,漏接后结束。
5. 暂不加入上下控制、角度、道具、背景故事和三维资产。
6. 分别记录自动测试、窗口检查和真实 AirPods 灰测。

请先提出可观察的验收标准,再开始实现。

结语

《颠了个球》最后留下了一款游戏,也留下了一条更重要的经验:

AI 让版本快速出现,真实试玩决定版本是否值得留下。

当我们不断追问“动作自然吗、球有节奏吗、画面像真的在飞吗、还愿意再玩一局吗”,一副耳机才真正从传感器设备,变成了游戏控制器。