Files
kidgo/.workbuddy/memory/2026-10-20.md
T
2026-10-20 22:21:26 +08:00

6.3 KiB
Raw Blame History

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 4480、h 1932),耐久 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 冰→钢丝→移动平台→竖井→钢丝
    • 存档点压到 37 个(原 47 且关卡短一半),坑宽 46 格,落脚点 12 格
  • 新增工具: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 双击脚本)