live试玩round1+round2修复: 引擎gesture新增tapcancel(immediateTap误判定回滚,默认关闭零行为变化)+ rhythm判定账簿/超时Miss复活/菜单1s去抖/正片下滑永不弹菜单 + space判定冷静期800ms/暂停恢复3颗缓冲/回大厅leaving收尾/hit_last在播标记 + storyline intro满播不掐/抉择25s兜底 + simon免费重听两层账 + escape教学链自动接播/90s高频零提示 + rpg擦伤零误报/tap不丢 + guide.test适配去抖期(语义不变)+ 文档同步(13号§9-§19/web-engine-api/README)。回归:storyline82/escape140/simon132/rhythm141/space155/rpg188/guide122共921断言全绿

This commit is contained in:
xsl
2026-09-01 22:00:00 +08:00
parent 624fec8a23
commit 4e9970e227
22 changed files with 3161 additions and 401 deletions
+23 -2
View File
@@ -100,7 +100,10 @@ iOS Safari 和 Chrome 都要求 AudioContext 必须在一次真实用户手势
3. 遮罩移除,Promise resolve → 你调 `game.start()` 接管。
**任何 `play()` 在解锁前调用只会 console.warn 并跳过。**
解锁后再 `attachGestures`,这样开始那次点按不会被游戏误收。
解锁后再 `attachGestures`。开始那次点按不会被游戏误收,引擎双保险:
遮罩对触摸 `preventDefault`(不再合成鼠标事件),且 gesture.js 在
document 捕获级记录全页面最后一次触摸,700ms 内忽略一切合成鼠标回退
(早期版本只靠 root 级守卫,解锁 tap 的合成 click 会穿透成"单击跳旁白")。
## engine/audio.js — `WebAudioEngine`
@@ -277,7 +280,8 @@ vibrate([50, 30, 50]); // 自定义震型
```js
const detach = attachGestures(element, onGesture, config?)
// onGesture: (g: 'up'|'down'|'left'|'right'|'tap'|'doubletap'
// |'longpress'|'longpressend') => void
// |'longpress'|'longpressend'|'tapcancel') => void
// ('tapcancel' 仅 immediateTap 模式补发,见下)
// detach(): 解除绑定
```
@@ -290,6 +294,7 @@ const detach = attachGestures(element, onGesture, config?)
| `doubleTapWindow` | 300 ms | 双击判定窗口 |
| `tapSlop` | 24 px | 位移小于此才算点按 |
| `longpressMs` | 450 ms | 按住不动超过此值触发 `longpress` |
| `immediateTap` | false | true = 按下即发 tap,不等双击窗口(演奏类 opt-in,见下) |
- 方向取位移较大的轴,斜滑归一到最近主轴(防斜滑误判)。
- 触摸 + 鼠标双支持(桌面可用鼠标拖拽/点击调试);多指触摸忽略。
@@ -312,6 +317,22 @@ const detach = attachGestures(element, onGesture, config?)
"继续剧情""检查房间"这类非即时操作(Android 端 reflex/storyline/escape
均如此设计)。
**例外(opt-in):`attachGestures(root, cb, { immediateTap: true })`**——
按下(touchstart/mousedown)立即回调 `tap`,不等双击窗口;若 300ms 内
第二下也成立为点按,松开时补发 `doubletap` 改判(第一次 `tap` 已发出,
是否撤回其效果由游戏层决定,如 rhythm 多打一下 kick 无害)。代价:每次
按下都先发 `tap`——若该次按下最终判成滑动/长按,这声 `tap` 不可撤回,
所以只有"按下即出声"的演奏类游戏(rhythm)应开启,默认 false 行为不变。
**`tapcancel`(仅 immediateTap 模式补发,live round2 新增)**:序曲 `tap`
的声音收不回,但**判定可以撤回**——本次按下一旦确定不再是点按(移动超
`tapSlop` 转滑动、`longpress` 成立、触摸被 cancel;无 move 事件的快滑在
滑动事件之前补发),引擎补发 `tapcancel`,游戏层应撤销该次 tap 的判定/
统计(rhythm 据此把爆音移出判定链)。双击序曲(`tap`+`tap`+`doubletap`
序列)**不发** `tapcancel`——归属撤销由游戏层按双击窗口自行处理。
`onGesture` 签名因此扩为:`'up'|'down'|'left'|'right'|'tap'|'doubletap'|
'longpress'|'longpressend'|'tapcancel'`。
## engine/game.js — `Game` / `StateMachineGame`
```js