小游戏
这个游戏有两层互动,别混起来:
| 层 | 玩家做什么 | 频率 | 在哪 |
|---|---|---|---|
| 轻解谜(主层) | 看线索,选一个出口 | 每关 2~3 次 | 关卡与解谜 |
| 小游戏(这一份) | 动手,做一次精准操作 | 每关 0~1 次 | 本文 |
轻解谜是"想",小游戏是"手"。一层管脑子,一层管手感。
一、小游戏在这里是干什么的#
零操作是这个游戏的立身之本(见 玩法),所以小游戏不能是"必须过关的关卡",否则零操作就成了空话。它承担的是另外三件事:
| 它解决的问题 | 怎么做 |
|---|---|
| 纯"看"玩久了会腻 | 给一段有反馈的手部动作,节奏上有张有弛 |
| 玩家的手一直闲着,会觉得"我在干嘛" | 每关给一次"我做到了"的成就感,而且只要几秒 |
| 解谜是慢的,需要一点快节奏调剂 | 小游戏是快的(10~20 秒),和解谜形成对比 |
小游戏不是"关卡",是"插曲"。 它随时可以不做、做砸了也不影响任何东西—— 它的全部价值是让玩家在这十几秒里手感舒服。
二、三条准入标准(不满足就不做)#
任何新小游戏要进这个游戏,必须先过这三条:
| # | 标准 | 为什么 |
|---|---|---|
| 1 | 操作只有一种(点击 / 按住 / 滑动三选一) | 两种操作 = 两种要教的东西。玩家在插曲里不想学新东西 |
| 2 | 10~20 秒能结束 | 超过 20 秒它就从"插曲"变成"关卡"了,会打断主循环 |
| 3 | 零失败:做错不扣任何东西,随时能重来 | 铁律。而且要不设失败状态——不是"失败后可以重试",是根本没有失败 |
"失败了可以重试" 不是零失败,它仍然是失败——玩家会觉得自己搞砸了。 零失败的正确写法是根本没有"错"这个状态:敲错顺序 = 顺序重置,继续敲, 中间不弹任何"你错了"。见下面第三节第 4 条。
三、跳铃铛(第一个实现的小游戏)#
3.1 长什么样#
兔子跑到一处挂着铃铛的地方会自动停下。三个铃铛挂在不同高度,兔子站在下面。
🔔 ← 高(102px)
🔔 ← 中(74px)
🔔 ← 低(46px)
🐰 ← 兔子站在地面
卡片告诉玩家按什么顺序敲响(比如 2 → 3 → 1)。
3.2 怎么玩#
| 步骤 | 玩家 | 兔子 |
|---|---|---|
| 1 | 按住屏幕 | 蹲下蓄力,脚下出现一根蓄力条 |
| 2 | 按到想要的力度 | 蓄力条越长,起跳速度越大 |
| 3 | 松手 | 起跳,高度由蓄力决定 |
| 4 | 看它够到哪个铃铛 | 到最高点时敲响最近的那个铃铛 |
| 5 | 按卡片顺序敲完三个 | 藤蔓让开,继续往前跑 |
蓄力 → 高度的换算(v = 300 + 蓄力秒数 × 700,上限 900):
| 目标铃铛 | 需要高度 | 需要初速 | 需要按住 | 容差 |
|---|---|---|---|---|
| 低 🔔 | 46px | 407 | 0.15 秒 | ±13px(约 ±19ms) |
| 中 🔔 | 74px | 516 | 0.31 秒 | ±13px(约 ±18ms) |
| 高 🔔 | 102px | 606 | 0.44 秒 | ±13px(约 ±18ms) |
3.3 「一跳一铃」——这里踩过一个真坑,必须记下来#
最初写的是"兔子的高度经过某个铃铛就敲响"。听起来很自然,但它有个致命问题: 跳跃是穿过所有高度的。
一次蓄力到 102 的跳,路上会依次经过 46 和 74,于是三个铃铛一起响。 "按住多久跳多高、敲哪一个"这个玩法完全不存在——不管按多久都是全响。
修法:只在最高点判定一次。
兔子开始下落的那一帧(vy <= 0)比较一次高度,取最近的那个铃铛。
因为铃铛间距 28px > 容差 13px,最高点只可能匹配到一个铃铛。
验收是硬的:tools/art/notes_check.mjs 会记录每次跳跃敲响了几个铃,
实测 1,1,1——一次跳跃只敲一个。这个数不达标就是玩法坏了。
3.4 敲错了会怎样(零失败)#
| 情况 | 发生什么 |
|---|---|
| 敲的顺序不对 | 顺序进度重置为 0,已经敲响的灯全部熄灭,飘一句"顺序不对,再听一次"。不扣东西、不弹失败 |
| 力度不够,没够着任何铃铛 | 飘一句"没够着,再来一次"。不扣东西 |
| 敲对了但还没敲完 | 进度 +1,对应的灯亮起,飘"叮 —— 第 N 个对了" |
奖励:每敲响一个铃铛 +1 种子(不管顺序对不对,敲响就有);三个都按对顺序敲完 +8 种子 + 一处收集。
如果只有"全对"才给东西,那敲错的三次就是白费——玩家的感受是"我亏了"。 改成敲响就 +1,于是每一次操作都有回报,敲错只是"没往前走"而不是"倒退了"。 和折返路上塞东西是同一个道理(见 关卡与解谜 第五节)。
3.5 为什么是"跳铃铛"而不是别的#
选它是因为它恰好用了游戏里已经有的东西:
| 已有的 | 怎么被复用 |
|---|---|
跳跃物理(g=1800) |
蓄力跳同一套物理,不用为小游戏另写一套运动 |
| 序列帧动画 | 复用 hop / leap 两档,不用新做动画 |
| 兔子的性格(爱蹦) | "没东西也跳一下"这条设定(玩法 4.3)在这里变成正式玩法 |
| 铃铛的"叮" | 一个音高对应一个高度,音高就是线索——不用额外做 UI 告诉玩家敲对了 |
三个铃铛是三个音高,敲对了顺序就是一小段上行/下行的旋律。 玩家不用看进度灯也能听出自己敲到第几个——听觉自己就是反馈。 这是最省成本的一种"高级感":不需要美术、不需要 UI,只要三个不同的音。
四、十条候选(按实现成本排)#
都在"操作一种、10~20 秒、零失败"这三条之内。跳铃铛已实现,其余按需再加。
| # | 名称 | 操作 | 玩法一句话 | 复用到的已有系统 | 成本 |
|---|---|---|---|---|---|
| 1 | 跳铃铛 ✅ | 按住蓄力 | 按卡片顺序敲响三个不同高度的铃铛 | 跳跃物理 + 序列帧 | 已实现 |
| 2 | 接露珠 | 点击 | 花瓣上滚下的露珠,点一下接住,掉进手账 | 收集系统 | 低 |
| 3 | 蒲公英 | 按住吹气 | 按住不放,种子飘得越远;松手看它落在哪 | 风力(已有芦苇/种子线索) | 低 |
| 4 | 影子配对 | 点击 | 三株植物的影子打乱,点影子配到本体 | 30 种植物素材 | 中(要画影子) |
| 5 | 溪流放叶子 | 点击时机 | 叶子漂过石缝,在正确的时机点一下让它拐弯 | 溪流段落 | 中 |
| 6 | 数花瓣 | 点击 | 转动的花,数清有几瓣,点对应数字 | 花的形态(5 类) | 低 |
| 7 | 敲树干听虫 | 点击 | 敲三下,听回声判断虫在哪一侧 | 音效 | 中(要音效) |
| 8 | 摆石子过溪 | 拖动 | 拖石子到溪里,摆出能踩的路 | 地形系统 | 高(要拖拽) |
| 9 | 追萤火虫 | 滑动 | 滑动引导兔子追光点,碰到就收 | 光照系统 | 中 |
| 10 | 织草绳 | 长按节奏 | 按节奏长按,把草编成绳 | 节奏音 | 中 |
否掉它的是两条,不要用"拖拽违反操作只有一种"当理由——那句话站不住, 因为拖动本身就是一种操作(四类操作的判定口径见 小游戏库 第二节):
- 它是"先点选石子、再拖动放置",两个动作叠加,这才是"混合了两种操作类型";
- "摆出能踩的路"隐含了"摆错就过不去",那就是失败状态,违反准入标准第 3 条。
列在这里是为了说明标准真的会砍东西,不是摆着好看的。 把理由修正是因为:判据说不圆,标准就守不住——下一批设计一定会有人拿 "拖动也是一种操作"来抬杠。
五、小游戏在关卡里的位置#
| 项 | 定案 |
|---|---|
| 频率 | 每关 0~1 次,间隔 9000 |
| 触发 | 跑到小游戏点,兔子自动停下,面板从底部推上来 |
| 计时 | 没有。停多久都行 |
| 做完 | 面板收起,兔子继续往前跑 |
| 不做 | 没有"跳过"按钮,因为随时可以放着不管——不做就等于没做 |
| 和岔路的关系 | 不重叠。触发时比较"谁更近",先到的先开(见下) |
最初写成:
if (dist > nextBranchAt) { openBranch(); }
else if (dist > nextMiniAt) { openMini(); }
nextBranchAt 初值 2600、dist 从 0 开始,条件第一帧就成立 → 岔路立刻触发;
岔路结束后 nextBranchAt = dist + 5000,下一帧又立刻满足……
结果岔路把整局吃掉了,小游戏一次都没触发过。
正确写法是比较"谁更近",而不是各自比"到了没":
var dBranch = nextBranchAt - dist, dMini = nextMiniAt - dist;
if (dBranch <= 0 && dBranch <= dMini) openBranch();
else if (dMini <= 0) openMini();
这个 bug 在浏览器里跑 9 秒就能看出来(距离卡在 2600 不动),
光看代码看不出来——两个 if 看着都很合理。
六、验收(tools/art/notes_check.mjs)#
整套演示有 26 条硬验收(小游戏占 7 条),跑一次真浏览器全过才算数:
| # | 验的是什么 | 实测值 |
|---|---|---|
| 1 | 小游戏会自己触发(不是靠手动调用) | ✅ mode=mini |
| 2 | 面板真的翻开了 | ✅ on=true |
| 3 | 顺序卡出现(3 个铃铛) | ✅ 1-2-3 |
| 4 | 一跳一铃(一次跳跃不会连敲几个) | ✅ 每次敲响数 = 1,1,1 |
| 4.5 | 收集真的发生了(零操作下花自己进手账) | ✅ 摘到 6 朵 · 入账 1 · 瞥见 5 |
| 5 | 每次跳跃都敲中了(容差不是太紧) | ✅ 1,1,1 |
| 6 | 按对顺序能通关 | ✅ hit=[true,true,true] |
| 7 | 结束后回到跑动 | ✅ mode=run |
node tools/art/notes_check.mjs
它对应的就是 3.3 那个坑。玩法对不对,靠这一条数字守。
如果哪天有人改回了"经过就敲响",这一条会立刻变成 3,3,3 并报错。