# 2026-10-20 ## 项目:IWBT C 同人平台跳跃游戏(网站) 从零搭建 `/Users/cancerai/tmp/iwanna`,纯前端无依赖(原生 ES modules + Canvas 2D),致敬 I Wanna Be The Guy / I Wanna Be The Creator。 ### 架构 - `index.html` + `css/style.css` + `js/` - `js/util.js` 常量(TILE=32,房间 800x608) - `js/input.js` IWBT 键位(Shift/Space/Z 跳,X 射击,R 重开,Esc 暂停) - `js/player.js` 手感物理:**重力 0.4、跳 8.5、二段跳 7.6、水平 3、最大下落 9、松手即停无惯性、松跳削减高度** - `js/level.js` ASCII 网格解析 → tiles + 实体 + 事件系统(坑向触发器) - `js/boss.js` 三阶段 BOSS(guy / mecha / devil),BOSS 贴地活动以保证站地平射可命中 - `js/camera.js` 跟随 / 锁定 / 缩放(「摄像头」道具效果) - `js/levels/{tutorial,jump,endurance,boss,troll}.js` 共 **17 关**(教学2 + 跳刺5 + 耐久3 + BOSS3 + 坑向4) - `js/editor.js` 自制关卡编辑器(localStorage 存档、导入导出 JSON) - `js/save.js` localStorage 通关记录 ### 关卡 ASCII 符号约定 `#@=M` 方块变体、`%` 冰、`~` 水、`-` 单向台、`V` 消失块、`*` 美味水果(即死)、 `^v<>` 四向刺、`S` 出生、`G` 存档、`X` 终点、`P` 手枪、`F` 苹果、`J` 跳刷新、`W` 传送门、 `T` 提示牌、`O` 炮台、`D` 掉落刺、`C` 摄像头区、`B` BOSS、`!` 事件触发器 ### 关键设计约束(改关卡时必须遵守) - 单次跳跃可上 **2 格(64px)**,二段跳可上 **3 格(96px)**,理论极限 ~5 格 - 平跳水平约 **4 格(128px)**,二段跳约 **6 格** - 平台所在行 row R,玩家站立的台面高度 = `R * 32` - 相邻平台垂直差 ≤ 96px、水平间隙 ≤ 96px 是安全设计区间 - 刺的碰撞盒比格子小(宽容判定) ### 测试工具(tools/) - `check.mjs` 关卡格式校验(行宽/字符/S/X 数量/炮台·传送门·BOSS 配置数量一致) - `smoke.mjs` 无头物理冒烟(构造全部关卡、模拟输入、死亡重生、BOSS 受伤、传送门) - `dom-smoke.mjs` mock DOM 跑通 main.js 入口 - **`solve.mjs` 可达性求解器**:用真实物理常量做 BFS(走/落/跳×二段跳时机/空中转向), 验证终点与每个存档点可达。水当作可站立面、移动平台整条路径当桥(乐观)。 **改关卡后必跑**,否则很容易做出无法通关的图。 运行:`node tools/xxx.mjs` ### 第二轮改造(用户反馈后) - 删除主菜单「开始游戏」按钮(点了没反应),只保留「关卡选择」进游戏 - 新增 `js/levels/kit.js`:`G(w,rows)` 自动补宽并把最后一列设为墙、`rep/E/plat/ent/flr` 辅助, **写关卡不要再手工数点**,用这些函数拼行 - 关卡全面加长加大:跳刺 5→**10** 关(w 44~80、h 19~32),耐久 3→4 关(e1 100 格 / e4 120 格), 共 **23** 关;BOSS 血量上调(guy 72 / mecha 96 / devil 128,阶段按血量比例触发不受影响) - `js/level.js` 掉落刺落地后 80 帧自动消失(否则会永久堵死落脚点) ### 第三轮:修存档点「没用」的 bug 根因:**R 键语义错**。IWBT 里 R = 自杀并回到最近存档点,但原实现把 R 绑成"整关重开" (`startLevel` 重建 Level+Player,`savePoint` 清零)。玩家死了本能按 R → 存档进度全丢 → 存档点形同虚设。 修复与改动: - `js/input.js`:`restart:['KeyR']` → `suicide:['KeyR']` + `restart:['Backspace']` - `js/main.js`:新增 `suicide()`(调 `player.kill()`,死亡数+1,自动回存档点);暂停菜单按钮 = 整关重开 - 重生后 `cam.snapTo()` 立即对准(原来镜头慢慢 lerp,看着像没回去) - `js/player.js`:存档点判定盒外扩 6px;激活时 toast「存档点 n/N 已激活」;respawn 加防卡墙顶出 - `js/level.js`:存档点编号 `e.no` + `totalSaves`;`js/ui.js` + HUD 新增 `存档 n/N` 指示 - `js/sprites.js`:未激活存档点加呼吸光环、颜色提亮(原来 `#5d4a72` 太暗看不清) - `js/levels/jump.js` j2:存档点 (6,22) 悬空导致重生即坠落,移到 (15,22) 平台上 新增测试:`tools/save-test.mjs`(存档点激活+重生全关卡)、`tools/key-test.mjs`(R/Backspace 语义回归)、 `tools/save-diag.mjs`(存档点放置环境诊断)、`tools/bot.mjs`(真实物理机器人试玩) ### 第四轮:跳刺关全面加大加难 + 去掉存档点 toast - 去掉「存档点 n/N 已激活」toast(user 要求);HUD 的 `存档 n/N` 指示保留 - `js/levels/kit.js` 新增 **`Grid` 网格构建器**(`.rect/.hline/.vline/.border/.out()`)和 `chain()`。 **大地图一律用 Grid 写,别再拼字符串**——104~150 宽的地图拼字符串必然错列 - 跳刺 10 关全部重写,尺寸 68×52 ~ 150×40(原来最大 80×32): - j1 104×20 刺海长廊+竖井收尾 / j2 68×52 高塔 / j3 140×22 一格宽钢丝(顶刺)+竖井 - j4 130×24 冰面+冰柱+竖井 / j5 120×24 移动平台+炮台+竖井 / j6 132×34 双层层+竖井 - j7 72×52 掉落刺高塔 / j8 130×26 冰→塌陷浮桥→钢丝→单向台上行 / j9 128×34 三层+苹果 - j10 150×40 冰→钢丝→移动平台→竖井→钢丝 - 存档点压到 3~7 个(原 4~7 且关卡短一半),坑宽 4~6 格,落脚点 1~2 格 - 新增工具:`tools/reach-diag.mjs`(按行打印可达 x 范围,定位断连)、 `tools/jumpdist.mjs`(用真实 Player 测跳跃距离)、`tools/bot.mjs`、`tools/save-diag.mjs` - `solve.mjs` 状态上限 6000→40000,并支持 `DJ=0,12,24` 环境变量收紧二段跳时机做鲁棒性检查 ### 关键物理实测(真实引擎,tools/jumpdist.mjs) 单跳水平 **138px(4.3 格)**;二段跳随时机 156 / 183 / 207 / 225 / **237px(7.4 格)**。 求解器按 127 / 206 算,**比真实引擎更保守 → 求解器判可达即真人必可过**。 ### 天花板刺摆放规则(踩过的坑) - 平台行 P,天花板刺放 **P-5**:允许完整单跳、二段跳必死 → 该段坑宽只能 ≤ 4 格 - P-3 只允许 rise<65px 的小跳;P-7 以外基本无效 - 竖井/塔的入口、转向处**不能放侧壁刺**,否则跳跃弧线正好撞上(j3/j4 都栽过) - 走廊尽头接竖井时,底座与走廊末块留 **3 格空隙、高差 2 行** 最稳 ### 运行方式 ES modules 需要 HTTP:`python3 -m http.server 8123`(已提供 `启动游戏.command` 双击脚本)