fix: 修复了冰块惯性问题和死亡效果

This commit is contained in:
cancerai committed 2026-10-22 19:43:01 +08:00
1 parent 47a462be01
commit 4b3dfd635b
15 files changed
+633 -38

No files matched your search

+57
View File
@@ -0,0 +1,57 @@
# 2026-10-22
## 工作区被清空后从对话记录重建 + 移动端虚拟手柄
- 本会话开始时 `/Users/cancerai/tmp/iwanna` 被清空(workspace_recovery 重建为空目录)。游戏全部源码并未丢失:完整对话记录在 `/Users/cancerai/.workbuddy/projects/Users-cancerai-tmp-iwanna/dd6693af-142a-4164-8c3a-fb33ab9626d8.jsonl`(约 7.9MB)。
- 重建方法:按 JSONL 行序重放所有 `Write`/`Edit` 调用;另有 7 处**通过 Bash 的 sed/perl 修改项目文件**(diamond→vgap、R-3→R-2、corner→block、j2 台阶加宽、3 个测试脚本 `mv` 进 `tools/` 并改 import)必须在对应行号处同步插值,否则 `jump.js` 大量 Edit 会 miss。重建后 `check.mjs` 与 `solve.mjs` 全部通过(23 关可达)。
- 新增移动端虚拟手柄:
- `js/input.js` 增加 `touch` 状态层 + `setTouch(action,bool)` / `clearTouch()`,与键盘共用 `now/prev` action 模型。
- 新模块 `js/touch.js`:左下 `◀▶` 移动、右下大「跳」键 + 🔫射击(按住连发)+ ▼下蹲 + 暂停/重生小键;pointer 事件 + `setPointerCapture` 支持多指;`show/hide/setGun`。
- `index.html` 加 `#touch` 覆盖层 + viewport 锁缩放;`css/style.css` 加 IWBT 风格半透明控件(含 `env(safe-area-inset-bottom)` 安全区)。
- `js/main.js`:触屏自动检测(`?touch=1` 强制开 / `?touch=0` 关);`startLevel`/`resume` 显示、`pause`/`finish`/菜单隐藏;HUD 同步 `hasGun` 控制射击键置灰;触屏射击走 `shoot()` 回调(受 `hasGun` 与冷却限制)。
- 验证:`node --check` 三个 JS 均通过;mock-DOM 运行测试确认按下/松开正确置位、pause/shoot 回调触发、hide 清理卡键;`check`/`solve` 仍全绿。
## 桌面端不显示触屏按钮(用户明确要求)
- 原来只靠 JS 切换 `#touch.hidden` 类来显隐,若某处误调 `Touch.show()` 桌面端会冒出按钮。
- 改为「CSS 硬兜底 + `<html>` 类门控」:仅当检测到移动端/触屏(`ontouchstart` / `maxTouchPoints>0` / `(pointer:coarse)`)或 `?touch=1` 时,给 `documentElement` 加 `.touch` 类;`css/style.css` 新增 `html:not(.touch) #touch{display:none !important}`。这样桌面端无论 JS 状态如何都绝对不显示,触屏设备才显示。`?touch=0` 仍可强制关。
- 验证:node 模拟 5 种环境(纯桌面/触屏笔电/手机/`?touch=1`/`?touch=0`),输出与预期一致;服务端 `css/style.css` 已含该规则。
## 修复:滑出冰块后惯性丢失
- 根因:`js/player.js` 水平移动里 `onIce` 每帧按当前位置重算;一旦脚下滑出冰面,`onIce=false` 走 `else` 分支 `this.vx = target`,把冰面攒出的速度瞬间归零。
- 修复:新增 `this.sliding` 状态。在冰上攒出 `|vx|>0.6` 即标记 `sliding=true`;离开冰后只要 `sliding` 且 `|vx|>0.05`,就用比冰面稍大的地面摩擦(0.25)缓慢收速,保留惯性;顺向按键保持/恢复满速、反向按键较快抵消;`|vx|<0.1` 才清零并退出滑行,恢复 IWBT 标准「松手即停」。`reset/respawn/kill` 三处都重置 `sliding=false`。
- 验证:node 模拟「冰上按右 20 帧→vx=3.60,滑出冰面松手后 12 帧 vx 由 3.35 缓降到 0.60 仍滑行」;`solve.mjs` 仍全绿(可达性未退化)。
## 二次修复:滑出冰块后仍「感觉不到惯性」(真正根因)
- 上次只解决了「离冰瞬间归零」,但残留两个致命点(用 `tools/ice-test.mjs` 实测暴露):
1. **空中也按地面摩擦 0.25 衰减**(`roadFric` 不区分空/地)→ 滑出冰块边缘掉下去时水平速度 7 帧内归零,根本飞不出去,这是玩家最直观感到「没惯性」的场景。
2. 松开键时 0.25 的强摩擦一下刹死(实测只滑 2.5px);而按住同向键反而每帧 +0.5 一路涨到 4.05 且永不回落,滑行比走路还快。
- 现在 `PHY` 新增滑行常量:`SLIDE_FRIC 0.12`(地面收住)/ `SLIDE_AIR 0.02`(空中几乎无损)/ `SLIDE_EASE 0.14`(超速时收敛回走速)/ `SLIDE_BRAKE 0.45`(反向刹车)/ `SLIDE_MIN 0.35` / `SLIDE_STOP 0.05`;`ICE_MAX = HSPD*1.35`。
- 行为约定:空中保留惯性;地面缓慢收住(4.05→0 约 34 帧 ≈ 70px);按住同向键只让超速**收敛**回 3(不再加速),低于走速即交还玩家控制;反向键刹车后立刻反向;撞墙/归零即退出滑行。
- 另加可视化反馈:超速滑行且着地时每 5 帧在脚下溅 2 粒冰屑(`js/player.js`),让惯性看得见。
- 新增回归测试 `tools/ice-test.mjs`(4 个场景 + 断言,退出码 0/1),已并入全套:`check / solve / smoke / save-test / key-test / dom-smoke` 全绿。
- 顺手补了 `tools/key-test.mjs` 与 `tools/dom-smoke.mjs` 缺失的浏览器桩:`location`、以及移动端手柄引入后需要的 `navigator`(用 `Object.defineProperty` 覆盖,Node 22 只读) 与 `matchMedia`——这两个脚本此前在 Node 22 下直接崩,现在可跑。
## 死亡即复活(无死亡动画)
- 用户要求:kid 死亡后立刻复活,不要死亡动画。
- `js/player.js`:`kill()` 去掉粒子爆炸、震屏、死亡张嘴,只留 `sfx.death()` 与 `onDeath` 计数;`update()` 顶部 `if (this.dead) this.respawn();` 让 R 键自杀/假存档触发的死亡下帧即复;边界坠落与 `deadlyRect` 两处致死点改为 `kill()` 后**同帧** `respawn()` 并 `return`(不再等 34 帧的死亡动画);`respawn()` 末尾置 `justRespawned=true`。
- `js/main.js`:复活帧镜头 `snapTo` 瞬移(`wasDead && !P.dead` 或 `justRespawned`);删除渲染里的死亡血色闪烁 `rgba(200,0,40,.18)`。
- `js/sprites.js`:删除 `p.dead` 时的张嘴绘制。
- 新增回归测试 `tools/death-test.mjs`(10 项断言:同帧复活/无粒子/无震屏/justRespawned/连续 5 次/R 键自杀后立即可操作/30 帧无特效残留)。全套 7 个测试仍全绿。
## 死亡地点加 IWBTG 风格血迹效果(用户追加要求)
- 需求:即时复活仍保留,但在**死亡地点**留下模仿 IWBT(I Wanna Be The Guy)的 kid 死亡效果(血腥飞溅)。
- 实现:在 `js/level.js` 新增常驻 `deathMarks` 列表(仅 `reset(true)` 整关重开时清空,`softReset()` 不碰 → 复活后血迹仍在)。
- `addDeathMark(x,y)`:在死亡坐标落一处血洼 + 10 个卫星血点(基于 seed 的稳定伪随机,形状逐帧一致),外加 18 个红色方块飞溅碎块(自带 parts 列表,受重力、~32 帧后消失);最多保留 60 处。
- `drawDeathMark(ctx,dm)`:世界坐标下绘制常驻血迹 + 动画碎块;放在 `draw()` 的实体/子弹之后、全局粒子之前。
- `update()` 末尾演化各 mark 的碎块(重力、衰减、移除死亡碎块),血点常驻。
- `js/player.js` 的 `kill()`:`sfx.death()` 后调用 `this.level.addDeathMark(this.cx, this.cy)`(死亡坐标)。仍不触发震屏/屏幕级粒子(保持「无死亡动画」)。
- `tools/death-test.mjs` 扩充到 14 项:新增「死亡地点留下 IWBT 血迹」「30 帧后血迹常驻」「多次死亡血迹累积」「整关重开清空」。全套仍全绿。
## 死亡血迹改为「只显示上一次死亡」(用户再次调整)
- 用户要求:多次死亡时只显示**上一次**死亡的血迹,不要累积成尸体堆。
- `js/level.js` 的 `addDeathMark` 改为新增前先 `this.deathMarks.length = 0`(清掉旧血迹),所以任意时刻场上仅保留最近一次死亡的血迹;整关重开 `reset(true)` 仍清空。
- `tools/death-test.mjs` 地图 row2 加第二枚刺(col10),测试 5 改为「4 次死亡(前后两处)后 `deathMarks.length === 1` 且位于最后一次 col10」,验证只保留上一次。全套仍全绿。
## 死亡标记从血迹改为「红叉(X)」(用户再次调整)
- 用户要求:把死亡效果从血洼/血点/飞溅碎块改成简单的**红叉**即可。
- `js/level.js`:`addDeathMark` 只记 `{x,y}`(不再生成 parts 碎块);`drawDeathMark` 改为画一个带深色描边的红色 X(半边长 9,lineWidth 5 黑描边 + 3 红主体);`update()` 去掉碎块演化循环(红叉为常驻静态标记)。仍只在死亡地点显示、只保留上一次、整关重开清空。
- `tools/death-test.mjs` 措辞「血迹」→「红叉」。全套仍全绿。
+14
View File
@@ -5,6 +5,12 @@
- 玩家判定盒 **11×21**(`Player` 构造函数里 `this.w = 11; this.h = 21`)。改这里会同时影响 `level.js` 的 `spawn` 偏移和全部可达性工具。
- 刺判定盒在 `js/level.js` 的 `spikeBox()`,已按刺尖收紧(上刺 y+8 起、左右刺 x+5 起等),留出像素级擦弹窗口。`T.KILLER` 必须有兜底 return,否则 `deadlyRect` 会在坑向关崩溃。
## 冰面滑行惯性(易反复踩坑)
- 冰面物理只在 `js/player.js` 的水平输入段,`sliding` 是唯一惯性开关:冰上 `|vx|>SLIDE_MIN` 置位,离冰后仍按 `sliding` 走惯性分支,撞墙/归零/交还控制时才清位。
- **空中与地面必须分开摩擦**:`SLIDE_AIR 0.02`(几乎无损,能滑出冰块边缘飞出去)vs `SLIDE_FRIC 0.12`(地面约 34 帧/70px 收住)。曾经两者都用 0.25,导致「滑出冰块掉下去 7 帧就没速度」——玩家最直观的「没惯性」就来自这里。
- 按住同向键只能让超速**收敛**回 `HSPD`(`SLIDE_EASE`),不能继续加速;低于走速立刻 `vx=target` 交还控制。反向键 `SLIDE_BRAKE` 刹车后马上反向。
- 回归测试:`tools/ice-test.mjs`(冰→普通地面/悬空/起跳 4 场景 + 断言),改冰面物理必跑。
## 关卡设计硬规则(都踩过坑)
- `tools/solve.mjs` 是唯一权威:改完任何地图必须跑到「✅ 所有关卡终点与存档点均可达」。
- 之字竖井用 `needle.js` 的 `well(xL, xR, topRow, botRow, floorSide)`:每级升 2 格、台阶宽 5 且两级重叠 2 格、顶台与上层地板**同高相接**、靠地板侧井壁从 topRow+1 到 botRow-4(底部留 3 格入口)。
@@ -23,3 +29,11 @@
## 关卡现状(23 关)
跳刺 j1~j10(68×52 ~ 150×44,刺密度 3.8%~11.6%)、耐久 e1~e4、boss b1~b3(可达性求解跳过)、坑向 r1~r4。
## 移动端虚拟手柄(2026-10-22 新增)
- 触屏自动检测:`matchMedia('(pointer: coarse)')` / `ontouchstart` / `maxTouchPoints>0`;`?touch=1` 强制开、`?touch=0` 关(桌面预览用)。
- `js/input.js` 的 `touch` 状态层 + `setTouch/clearTouch` 与键盘共用 action 模型(`now/prev`),不破坏可达性工具。
- `js/touch.js`:左下 `◀▶` 移动、右下大「跳」+ 🔫射击(按住连发,受 `hasGun`/冷却限制)+ ▼下蹲 + 暂停/重生小键;pointer 事件支持多指,`hide()` 会 `clearTouch()` 防卡键。
- 仅在 `playing` 态显示(`startLevel`/`resume` 显示,菜单/暂停/结算隐藏);HUD 每帧 `Touch.setGun(P.hasGun)` 控制射击键置灰。
- 若改键位含义,记得同步 `js/input.js` 的 `BIND` 与 `touch.js` 的 `HELD`/`api` 映射。