Files
kidgo/tools/jumpdist.mjs
T
2026-10-20 22:21:26 +08:00

55 lines
2.2 KiB
JavaScript

// 用真实引擎(Player/Level)测量跳跃水平距离,核对求解器的物理近似是否可信
const handlers = {};
const addL = (t, fn) => { (handlers[t] = handlers[t] || []).push(fn); };
globalThis.addEventListener = addL;
globalThis.window = { addEventListener: addL };
const key = (t, c) => (handlers[t] || []).forEach(fn => fn({ code: c, preventDefault() { }, repeat: false }));
const { Level } = await import('../js/level.js');
const { Player } = await import('../js/player.js');
const { Input } = await import('../js/input.js');
// 一块 40 格长的平地,右端断开
const flat = {
id: 'test', name: 'T', type: 'jump', width: 44,
grid: (() => {
const rows = [];
rows.push('#'.repeat(44));
for (let i = 0; i < 16; i++) rows.push('#' + '.'.repeat(42) + '#');
rows.push('#' + '#'.repeat(42) + '#'); // 整条实心平台
rows.push('#'.repeat(44));
return rows;
})(),
};
function measure(djFrame) {
const L = new Level(flat);
const P = new Player(L);
P.x = 8 * 32; P.y = 16 * 32 - P.h; // 站在 row17 平台上(平台行 17)
P.vx = 0; P.vy = 0; P.grounded = true; P.jumps = 0;
const x0 = P.x;
key('keydown', 'ArrowRight');
key('keydown', 'ShiftLeft');
let f = 0, landed = -1, released = false;
for (let i = 0; i < 200; i++) {
f++;
// 二段跳需要先松开再按下(跨帧),否则不算一次新的按下
if (djFrame && f === djFrame) { key('keyup', 'ShiftLeft'); released = true; }
if (released && f === djFrame + 1) { key('keydown', 'ShiftLeft'); released = false; }
L.update(1, P, null);
P.update(1);
Input.endFrame();
if (i > 2 && P.grounded) { landed = P.x; break; }
if (P.dead) break;
}
key('keyup', 'ArrowRight'); key('keyup', 'ShiftLeft');
Input.endFrame();
return { dist: landed >= 0 ? landed - x0 : null, f };
}
console.log('真实引擎跳跃水平距离(起跳点 x 到落点 x,单位 px / 格)');
for (const dj of [0, 6, 12, 18, 24, 30]) {
const r = measure(dj);
console.log(` 二段跳时机=${String(dj).padStart(2)} 帧 -> ${r.dist === null ? '未落地' : r.dist.toFixed(1) + 'px = ' + (r.dist / 32).toFixed(2) + ' 格 (' + r.f + ' 帧)'}`);
}