22 KiB
22 KiB
2026-10-23 工作日志
首页移除「清除存档」按钮(用户要求)
- 需求:把首页(scr-title)的「清除存档」按钮删掉。
- 改动:仅删
index.html:41的<button class="btn small" data-act="wipe">清除存档</button>一行。js/main.js里的wipehandler(确认后Save.wipe())已无按钮触发,成为未接线死代码,保留无害(用户只要求删按钮,未要求删逻辑)。 - 验证:
grep 清除存档/data-act="wipe"零残留;tools/无引用;death/map/check/solve/smoke/save/key/dom-smoke/ice九套回归全绿;预览 8123 返回 200。
主界面新增「设置」功能(用户要求)
- 需求:主界面加「设置」入口;可自定义用户名;可拖动调整手机操作按钮的位置与大小;点「确定」保存、有「恢复默认」按钮。
- 新增
js/settings.js:BUTTONS(7 个手柄键 left/right/jump/shoot/down/pause/suicide,各带默认cx,cy,size百分比+像素),meta()/place()/apply()/showUsername(),Settings.init/enter/confirm/reset,wire()给预览按钮绑「拖动移动 + 右下角圆点缩放」。工作副本work未点确定不落盘,点「返回」即丢弃。 - 存档:
js/save.js的settings:{username,touch}走Save.flush()(localStorageiwbtc_fan_v1)。 - 接线:
index.html标题菜单加「设置」按钮 +#title-user+scr-settings屏(用户名 input +#touch-preview拖拽预览 + 确定/恢复默认);js/main.js加settings/set-ok/set-resetaction 并Settings.init();js/touch.jsshow()里Settings.apply()让自定义布局在游戏内生效。 - CSS:
.tc-btn改为position:absolute;transform:translate(-50%,-50%)(active 变形同步保留 translate);新增.tc-rsz缩放手柄、.touch-preview、.set-*、#title-user样式。 - 验证:新增
tools/settings-test.mjs(默认布局/apply 定位/enter+confirm 保存用户名并写 localStorage/reset 回出厂且不自动落盘,15 断言全过);dom-smoke(main 初始化成功)、check/smoke/map/save/key/solve/ice 全绿;预览服务返回 200 且含scr-settings。
修复:拖动设置里的手柄按钮时直接跳到右下角
- 根因:
js/settings.js原wire()用「指针(clientX) − 基准偏移(off/cx0)」做绝对坐标映射定位;一旦wrap.getBoundingClientRect()取值与渲染偏差(预览百分比定位 + translate(-50%,-50%) 场景下易偏),按下首帧就把 cx/cy 算到极值 → 弹到右下角。 - 修复:改为相对位移累加——按下只记录
lastX/lastY与预览rect;pointermove把「相对上一帧的像素位移 ÷ rect 宽高 ×100」累加到当前 cx/cy(clamp 397%),缩放则按「当前指针到中心距离 ÷ 按下时距离」缩放160px)。第一帧必然停在正确位置,不再跳角。还顺手加了size(24e.stopPropagation+ try/catch 包裹setPointerCapture。 - 回归:
tools/settings-test.mjs新增「模拟拖动/缩放」4 条——派发真实 pointerdown/move/up 事件断言:cx 按位移增量变化、逆向可精确归位、缩放放大尺寸且不改位置。settings-test 共 21 断言全过,dom-smoke 初始化正常。
修复:设置界面打开后一片空白
- 根因:
js/ui.js的SCREENS数组没加'scr-settings',UI.show('scr-settings')会把所有已知屏 hidden、却从不取消设置屏的 hidden——只剩画布标题背景(用户截图即此症状)。 - 修复:SCREENS 加入
scr-settings(一行)。教训:新增 screen 必须同步 SCREENS 数组。 - 回归:
tools/settings-test.mjs增加「SCREENS 含 scr-settings / 标题有设置入口」两条源码断言,防复发;settings-test 与 dom-smoke 全绿。
设置界面:确定返回上一页 + PC/移动端分设
- 需求:① 点「确定」后返回进入设置前的界面(上一页);② 电脑端设置「键盘映射」,移动端设置「手机操作按钮」(按设备二选一显示)。
js/input.js:新增DEFAULT_BIND(出厂 BIND 快照)、export defaultBindings()(深拷贝)、export applyBindings(map)(用{action:[code]}覆盖生效键位)。js/settings.js:- 新增
KEY_ACTIONS(left/right/up/down/jump/shoot/suicide/restart/pause 共 9 项可重绑)、keyLabel(code)(←/→/Shift/空格等可读名)。 pcMode = !isTouch(由Settings.init(useTouch)传入);enter()末尾按pcMode切换#set-keys-row/#set-touch-row的 hidden:PC 显键盘映射、移动端显手柄按钮。renderKeys()渲染每行「动作名 + 按键 chip(点×删除)+ +添加」;wireKeys()点击委托 +documentkeydown 捕获下一次按键写入work.keys(Esc 取消),同一动作支持多键。confirm()保存用户名/touch/keys 并applyBindings(s.keys);reset()把 keys 也回出厂;makeDefaults()/meta()保证keys字段齐全。wireKeys()加keysWired幂等保护(重复 init 不会重复绑监听)。
- 新增
js/main.js:模块级let settingsFrom='scr-title';settingsaction 里settingsFrom = UI.cur后Settings.enter();set-ok改为Settings.confirm(); UI.show(settingsFrom)(确定后回上一页);Settings.init(useTouch)传入设备检测结果。index.html:设置屏新增#set-keys(键盘映射容器)+ 两行#set-keys-row/#set-touch-row供切换显隐。- CSS:
.set-keys/.krow/.kact/.kkeys/.kchip/.krm/.kadd/.klisten等键盘映射样式(blink动画提示监听中)。 - 验证:
tools/settings-test.mjs扩到 30 断言(input API / PC·移动端面板显隐切换 / 点击 chip→keydown 重绑 / 添加 / 删除 / 确定落盘 / reset 回出厂 / main 里 set-ok 返回上一页源码断言)全过;dom-smoke、check、smoke、map、save、key、ice、solve 全绿;预览 8126 返回 200 且含 set-keys/set-keys-row/set-touch-row。
设置·键盘重绑加冲突检测(用户要求)
- 需求:电脑端在设置里改键时,要能「检测冲突」。
js/settings.js:- 新增
let conflict = null(warning 状态)与conflictInfo(code, act, idx):遍历KEY_ACTIONS的work.keys,若code已被其它动作(或同动作不同槽)占用则返回该动作标签;并额外检查不可重绑的系统动作BIND.menu(如 KeyQ→「菜单」),避免把键绑到系统功能上。 onCaptureKey():捕获到非 Esc 的键后先查冲突;若冲突则拒绝写入、置conflict={code,label,act}并renderKeys()(停留在监听态,等换键或 Esc);无冲突才写入并清conflict。renderKeys():面板顶部加.kwarb红色横幅「「X」已被【Y】占用,请换一个键(Esc 取消)」;监听行 tip 在冲突时变红显示「该键冲突,请换一个键」。enter()/reset()重置conflict=null;Settings暴露_conflict()/_listening()供测试/调试。
- 新增
js/input.js:const BIND→export const BIND(供冲突检测引用menu键)。- CSS:
.kwarb(红底警示横幅)、.klisten.bad(红字警告)。 - 验证:
tools/settings-test.mjs扩到 36 断言,新增「冲突:jump 不会绑到被左移占用的 KeyA / 记录到被左移占用 / 顶部警告横幅 / 改用空闲键 KeyK 成功 / 同动作重复键不重复写入 / Esc 退出无残留 / KeyQ 被系统菜单占用」全过;dom-smoke、key、smoke、check、map、save、ice、solve 全绿。
刺改为「单三角形 + 碰撞与形状完全对应」(用户纠正)
- 用户纠正:不要 3 个小齿刺,要单个三角形占一格,且碰撞必须和三角形形状完全对应(不是矩形近似)。
js/sprites.jsdrawSpike:由「3 个小三角」改为一个占满整格的三角(尖端朝 dir,底边贴相反侧),并沿底边画一条暗色基座(正好落在三角底边,不扩大判定)。TILE=32,故每刺格 32×32。js/level.js:删除spikeBox(旧「收窄矩形近似」),新增spikeTri(t,x,y)(顶点与drawSpike完全一致)与triRectOverlap(分离轴定理,凸三角×AABB 充要条件)。deadlyRect改用spikeTri+triRectOverlap(网格刺 + dropspike 都用真三角;dropspike 用SPIKE_L与其drawDropSpike的 dir=2 一致)。- 关键性质:底宽尖窄——底部两角现在会致死(旧矩形近似把它们当安全),尖顶外侧安全,与真实三角形状一致。
- 一致性利好:game 的
player.js、tools/lib-reach.mjs(求解器)、save-diag.mjs、bot.mjs全部直接调用L.deadlyRect,改碰撞后自然统一,无需另改求解器模型。 - 验证:新增
tools/spike-test.mjs(16 断言:spikeTri 四向顶点 / triRectOverlap 内-外-AABB / deadlyRect 集成上刺尖顶安全·底中致死·底左角致死·空地安全 / 与旧矩形近似在底左角结论相反的精确性对比)全过;solve.mjs仍「所有关卡终点与存档点均可达」;editor-wysiwyg、dom-smoke、check、smoke、map、save、key、ice、settings 九套全绿。
编辑器所见即所得(WYSIWYG)+ 刺显示真实三角形
- 需求:编辑器里所有方块要显示游戏内真实样子(原来用「彩色块 + 字母/箭头」表示);刺要显示成三角形(游戏里
drawSpike本来就是三角,编辑器之前却画成↑↓←→箭头字符)。 js/editor.js:import * as S from './sprites.js';新增drawTileReal(ctx, ch, px, py, props, t)把每个字符分派到S.drawX(#/@/=/M→drawBlock 各 variant、%/~/-\/V/^/*/D/X/G/P/F/J/W/T/O/C各对应函数,^按props.dir转 0/1/2/3 画三角,S出生点画小人drawKid);B/!等无独立美术的保留「色块+字母」兜底drawMarker。draw()重写:每格调用drawTileReal替代「填色块+画字母」;保留淡网格线、选中高亮;传送门额外叠加W{id}与连接状态(编辑器元数据)。buildPalette():?工具用 ⌖ 字形,其余每个方块用drawTileReal渲染成 32×32 缩略 canvas(class="pal-cv"),调色板即真实预览。PALETTE改export const(便于测试);删除不再使用的SPIKE_ARROW。
- CSS:
.pal-cv{width:26px;height:26px;...image-rendering:pixelated}。 - 测试:新增
tools/editor-wysiwyg-test.mjs(init 渲染全部调色板方块无异常 / 刺以三角形图元绘制 lineTo=6 / 方块真实纹理 fillRect / 23 种方块在画布绘制不抛异常);dom-smoke(init 跑 buildPalette 覆盖全部 25 字符分发)、check/smoke/map/save/key/ice/solve 全绿。
设置·键盘重绑面板移除「下蹲」(用户要求)
- 需求:把「按键设置」里的下蹲(down)项去掉。
js/settings.js:KEY_ACTIONS删除['down','下(下蹲)'](由 9 项变 8 项)。仅影响键盘重绑面板与冲突检测范围;游戏内down逻辑(player.js下蹲/下穿)与移动端触屏down按钮(BUTTONS/touch.js/index.html的data-touch="down")保持不变——用户只要求移除「按键设置」里的项。- 测试:
tools/settings-test.mjs的跳跃行索引 4→3(down 移除后 jump 成为第 4 项),36 断言仍全过;dom-smoke、key、check、solve 全绿。
刺外观对齐参考图(冰蓝白渐变大三角)
- 用户反馈「这也不是三角形呀」并给参考图:经典 I wanna 的淡蓝白渐变单三角。此前刺是灰色
#c8c8d8+ 深色基座条,观感不像三角。 js/sprites.jsdrawSpike重写外观:顶点不变(仍与 level.jsspikeTri完全一致,碰撞不动),改为冰蓝白线性渐变(尖端#f4f9fd→ 底边#a8bccf,沿刺方向渐变)+ 淡蓝描边rgba(150,175,200,.9)+ 尖端附近白色高光片;删除深色基座条。- 删除已无引用的
tri()助手;C.spike/spikeDark更新为淡蓝(#cfdde9/#a8bccf)。 - 新增
spike-preview.html预览页:用真实 drawSpike/drawBlock 渲染地面刺/天花板刺/贴墙刺,供用户对照参考图。 - 测试:spike-test(16 断言)、editor-wysiwyg、dom-smoke、solve(23 关可达)及全部回归绿。
- 修复:上一轮 drawSpike 的「尖端高光」坐标计算错误(ax + (bx+cx)/2 把顶点当标量相加,多边形飞出格子),游戏中刺后面拉出灰色长条。已整体删除高光,只留冰蓝白渐变三角 + 淡蓝描边(与参考图一致)。全部回归绿。
冰面提速 + 删除首页操作说明 + 物理手感下调(用户要求三连)
- 冰面最大速度阈值调大:
js/player.js新增PHY.ICE_TOP: 1.6(原内联HSPD*1.35),冰面/滑行惯性速度上限 = HSPD×1.6;ICE_MAX局部变量改由PHY.ICE_TOP计算。ice-test 注释数字同步为 4.32。 - 删除首页操作说明:
index.html去掉 scr-title 的「操作说明」按钮和整个#scr-help屏;js/main.js删helpaction;js/ui.js的SCREENS删'scr-help';css/style.css删.help-*规则。 - 物理手感下调(用户:「kid移动速度减慢点,跳跃重力小一点」):
js/player.js与tools/lib-reach.mjs同步 GRAV 0.4→0.34、HSPD 3→2.7。扫描确认仅 j1/j9 终点不可达,按「调手感+重调关卡」方案修图:- j1 回廊:
carpet(m, 81, R-11, 15, [0, 8])—— x89 加缺口。原因:新物理下从 x88 跳台落进 x90 时落点偏右(fx≈2895),满跳第 3 帧就蹭 x91 的「>」刺尖;有 x89 就能走进 x90 从左侧起跳越过对刺门。 - j9 L0:
carpet(m, 5, 27, 116, [54])—— x59 加缺口。原因:矮洞天花板(row 24, x60-75)挡住 x57→x61 四格跳的横向漂移,玩家撞顶后下坠砸原点刺;拆成 57→59→61 两个 2 格跳(短跳 apex≈43px,碰不到洞顶)。 - j9 L1:
carpet(m, 9, 13, 108, [27])—— x36 加缺口。原因:hgap(36,13) 的通道列 36 被 carpet 填了 '^',整门是 3 连刺,从右往左回跳被 x37 的「<」刺别住;补缺口后恢复两段式穿越。
- j1 回廊:
- 全部回归绿:solve(23 关终点+存档+苹果全可达)、check/map/smoke/dom-smoke/save/key/spike/settings/editor-wysiwyg/ice/death/jumpdist/save-diag/bot 共 15 项。
- 教训(已入 MEMORY.md):① 4 格缺口跳在新物理下能跳但起拍位必须够靠左,跳台落点偏右是隐形杀手;② 实心天花板会挡高跳的横向漂移;③ hgap 通道列必须本身为空,carpet 缺口没对齐就会留一根 '^' 把门变成 3 连刺。
编辑器:去掉「查看属性」工具,改为任意工具点击即看属性(用户要求)
js/editor.js:PALETTE删除['?', '查看属性', ...](调色板 25→24 项,全是真实方块);buildPalette()删掉 '?' 的放大镜字形特例;act()删掉sel === '?'分支——现在所有点击都先selTile = {tx,ty}再renderProps(),配合原有「放置不覆盖」规则,拿任何非橡皮方块点击已有格子都只查看不覆盖;空格子则放置新块并显示其属性;橡皮擦与右键保持擦除语义。- 新增回归
tools/editor-pick-test.mjs(18 断言):假 DOM 的 addEventListener 真实收句柄,可模拟画布 mousedown;验证 PALETTE/UI 无 '?'、拿 ^/#/% 点 C/T 只查看不覆盖且面板渲染对应字段、拿 O 点空格放置并显属性、橡皮/右键擦除、空态引导文案。注意 renderProps 的字段是 appendChild 子行,断言要拼box.innerHTML + children.map(c=>c.innerHTML)。 tools/editor-wysiwyg-test.mjs清理 '?' 死过滤。- 全套 16 项回归绿(solve 23 关可达不受影响,本次只改编辑器交互)。
重力再降一档 + 去掉跳跃时 kid 身体晃动(用户要求)
- GRAV 0.34 → 0.33(
js/player.jsPHY +tools/lib-reach.mjs同步)。"稍微调低一点点":单跳 apex≈109px、滞空更久。扫参发现 0.32 断 j2、0.33 断 j6(非单调——6px 状态桶对齐决定窄窗存不存在),选最小步长 0.33。 - j6 修图:
m.set(44, 15, '.')清掉 hgap(43,15) 的 '<' 刺。根因:该对刺门原本只靠「tile45.3(fx≥1448)满跳→tile41.3」这条 2px 宽起拍窗通过;'<' 刺尖会别住起跳(fx<1448 第 3 帧即死),0.33 下 hop 落点整体左移 2px 窗口够不着。清掉后留 ^(43)+>(42) 双刺门,整格起拍窗 + 左右两个方向都稳。修后 j6 状态数 535→934。 - 去掉跳跃晃动:删
player.js起跳/二段的squash=1.28/1.22拉伸;sprites.js的drawKid改const sq = air ? 1 : p.squash——空中恒定原始体型,落地挤压(0.72)残余也不得带入空中。落地挤压保留(在地面,不属"跳跃时")。 - 教训(已入 MEMORY.md):① 对刺门的侧刺('<'/'>')会把起跳/落点窗切成 2px 级窄窗,求解器 6px 状态桶的对齐就能翻转整关可解性——改物理后遇到"某一跳全灭",先查是不是侧刺别住起拍;② 追这类问题用逐帧 trace(复刻 sim.jump 主循环打印每帧 x/y/vy + deadlyRect),比手算几何可靠得多。
- 16 项回归全绿;solve 23 关终点+存档+苹果全可达。
JUMP 8.5→8.0 降低跳跃高度(用户:「跳跃的高度太高了,调低点」)
js/player.jsPHY +tools/lib-reach.mjs同步:JUMP 8.5→8.0。单跳 apex 109.5→97px、水平 139→131px。只降 JUMP 不动 GRAV——保留用户之前要的「小重力飘」手感。- 扫参定位只破坏 j8(存档#4/#5+终点不可达)。逐帧 trace 实证:j8 x83-84 台(row18)→x88-89 台(row16) 这一跳的航线正对 vgap(86,14);
spikeTri的 '^' 顶点在格顶 y=480(无收紧),JUMP 8.0 单跳 apex 盒底 479——只差 1px,顶点 (2768,480) 在起跳后 t=21~25 共 4 帧落在玩家盒内,SAT 铁死。JUMP 8.5 时 apex 盒底 466 从容擦过。 - 修图(j8 两处,其余 22 关零改动):
vgap(m, 86, R-8)→m.set(86, R-9, 'v'):去掉 x86 地刺 '^'、保留压顶刺 'v'。试过「x88-89 台加宽到 x87-89」无效(上升段照样死,落点加宽治标不治本)。- x87 加 '-' 台(配合上面修完后的落点 2802 能上台)。
- 修后 j8 状态数 307→469,23 关终点+存档+苹果全可达。
- 16 项回归全绿。教训已入 MEMORY.md:① apex 盒底与航线 '^' 顶点差 1px 时顶点会连续 4 帧在盒内,改 JUMP/GRAV 后遇到「某一跳全灭」先算 apex 盒底对比航线刺顶点;② 逐帧 trace 复刻 sim.jump 主循环是定位这类问题的唯一可靠手段(手算 SAT 截面连错三次)。
JUMP 再降一档 + 下落分手重力 + 零形变(用户三连要求)+ 求解器加固(本次会话主体)
用户原话:「跳跃的高度太高了,调低点」→「跳跃高度再调低一点,下落时的重力加速度再明显点,而且kid落地时候依然有拉伸效果,还是不行」。
物理落地(js/player.js + tools/lib-reach.mjs 同步):
- JUMP 8.0→7.5:单跳 apex 97→85px、水平 131→118.8px(jumpdist 实测)。
- 新增
GRAV_FALL: 0.55:上升 0.33 轻、下落 0.55 重(分手重力)。落地前一帧竖直速度 8.9px/帧。 - squash 形变全部删除:
player.js去掉 squash 字段/起跳赋值/落地挤压逻辑;sprites.js的drawKid按原始 11×21 画;editor.js预览桩同步删字段。用户两次强调落地拉伸也不要。
求解器三个加固(tools/lib-reach.mjs,solve/reach-diag 默认值同步):
- RELSET [0,4,8,14] → [0,4,5,6,8,14]:vgap 夹缝需要"第 5/6 帧松键"的短跳(apex 32~43px 窗口)。实证:rel=4 t=7 盒右沿蹭地刺左坡 2.6px;rel=6 t=11 盒顶擦垂刺尖 0.6px;只有 rel=5 两边都躲开(j2 x16);j7 顶层 x52 夹缝则要 rel=6。
- fy 规范化(canon):落点常比地面高零点几 px(1535.5 vs 1536),与精确落点同 3px 桶——先占桶者赢,走链被错位几 px 的跳链顶掉。加 rel=6 后 j2 状态数从 1572 暴跌到 80 就是这么来的。push() 里按 canStand 门控吸附到 32 倍数。
- 状态桶 3px(上轮已改)。
关卡重调(js/levels/jump.js,核心矛盾:JUMP 7.5 + GRAV_FALL 0.55 下 3 连刺=96px 的起拍窗只有 0~1 帧,实测不可玩):
- carpet 缺口间距 4→3(3 连刺→2 连刺=64px,射程余量 13→55px):一行改动全局生效。
- j2 五处显式 gaps(井口/左端出口的 3 连刺必须拆,walk 限制使起拍位比所需位窄 1~2px):
- L0:
carpet(m, 5, 47, 50, [1, 18])保持(x25-27 已被间距 3 拆成 2 连刺)。 - L2:
[1, 11, 31]= x12 拆井口 3 连刺;x22/x42 补 hgap 夹缝通道列(原来被 carpet 填 '^',整门变 3 连刺且左侧落点是 4 格跳 128px 超射程)。 - L3:
[0]= x10 拆左端 3 连刺(左行跳落点盒右沿差 0.8px 蹭 x10 左坡;L1 同位置能过是因 vgap(12) 清空了 x10-14,L3 没有)。 - L4:
[1, 41]= x12 拆井口 3 连刺;x52 补 hgap 夹缝通道列。
- L0:
- 修后 j2 状态数 80→2421(10/10 存档+终点),23 关全可达。
验证:solve 23 关终点+存档+苹果全可达;check/map/smoke/dom-smoke/save/key/spike/settings/editor-wysiwyg/editor-pick/ice/death/jumpdist/save-diag/bot 共 15 项全绿。
GRAV 0.33→0.29 上升再飘一档(用户:「跳跃的重力参数再飘点」)
js/player.jsPHY +tools/lib-reach.mjs同步:GRAV 0.33→0.29(仅上升段,GRAV_FALL 0.55 不变)。单跳 apex 85→91px、水平 118.8→122px。- 本轮零修图:23 关全可达(状态数与上轮持平微增)。原因:上轮已把 carpet 缺口间距改 3 格(2 连刺=64px),射程余量充裕,上升更飘不触任何窄窗。
- 16 项回归全绿。至此手感链:移速 2.7 / 上升重力 0.29 / 下落重力 0.55 / JUMP 7.5。