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

+103
View File
@@ -0,0 +1,103 @@
// 冰面惯性回归测试:滑出冰块后必须保留惯性(空中几乎不衰减,地面缓慢收住)
const handlers = {};
const addL = (t, fn) => { (handlers[t] = handlers[t] || []).push(fn); };
globalThis.addEventListener = addL;
globalThis.window = { addEventListener: addL };
const key = (type, code) => (handlers[type] || []).forEach(fn => fn({ code, preventDefault() { }, repeat: false }));
const down = c => key('keydown', c);
const up = c => key('keyup', c);
const { Level } = await import('../js/level.js');
const { Player, PHY } = await import('../js/player.js');
const { Input } = await import('../js/input.js');
const W = 44;
// 地板 row 11:冰面 col1..6,之后 tail(普通地面或空)
function grid(tail) {
const g = [];
g.push('#'.repeat(W));
for (let y = 1; y <= 10; y++) {
g.push((y === 10 ? '#S' + '.'.repeat(W - 3) : '#' + '.'.repeat(W - 2)) + '#');
}
g.push('#' + '%'.repeat(6) + tail + '#');
g.push('#'.repeat(W));
g.push('#'.repeat(W));
return g;
}
let fail = 0;
const err = m => { console.log(' ✗ ' + m); fail++; };
function run(tail, label, releaseAt = 1e9, jumpAt = 1e9) {
const L = new Level({ id: 'ice', name: 'ICE', type: 'jump', width: W, grid: grid(tail) });
const P = new Player(L);
for (let i = 0; i < 10; i++) { L.update(1, P, null); P.update(1); Input.endFrame(); }
down('ArrowRight');
const log = [];
for (let i = 0; i < 90; i++) {
if (i === releaseAt) up('ArrowRight');
if (i === jumpAt) down('ShiftLeft');
if (i === jumpAt + 1) up('ShiftLeft');
L.update(1, P, null); P.update(1); Input.endFrame();
log.push({
f: i, x: +P.x.toFixed(1), vx: +P.vx.toFixed(2), y: +P.y.toFixed(1),
g: P.grounded, sl: P.sliding,
ice: P.grounded && L.iceAtBottom(P.x, P.y, P.w, P.h),
});
}
up('ArrowRight');
console.log(`\n=== ${label} ===`);
console.log('帧 x vx 地 滑 冰');
let crossAt = -1;
for (const r of log) {
const cross = !r.ice && log.find(o => o.f === r.f - 1)?.ice;
if (cross && crossAt < 0) { crossAt = r.f; console.log('--- 离开冰面 ---'); }
if (r.f % 3 === 0 || cross) console.log(`${String(r.f).padStart(3)} ${String(r.x).padStart(6)} ${String(r.vx).padStart(6)} ${r.g ? 'Y' : '.'} ${r.sl ? 'Y' : '.'} ${r.ice ? 'Y' : '.'}`);
}
const off = log.filter(r => r.f >= crossAt && crossAt >= 0);
const dist = off.length ? Math.max(...off.map(r => r.x)) - off[crossAt === 0 ? 0 : 0].x : 0;
console.log(`离冰时 vx=${off[0]?.vx ?? 0},离冰后位移=${dist.toFixed(1)}px`);
return { log, crossAt, off };
}
// A:一直按住右键冲出冰面 —— 惯性要把 4.05 平滑收敛到正常走速,不能瞬间刹停也不能一直超速
{
const { off } = run('='.repeat(34), 'A 冰面 -> 普通地面(一直按住右键)');
const v0 = off[0].vx, v10 = off[10]?.vx ?? 0, v30 = off[30]?.vx ?? 0;
if (v0 < PHY.HSPD) err(`A 离冰瞬间就丢了速度 vx=${v0}`);
if (v10 < 2) err(`A 离冰 10 帧后速度掉太快 vx=${v10}(应仍在滑行)`);
if (Math.abs(v30) > PHY.HSPD + 0.01) err(`A 离冰 30 帧后仍超速 vx=${v30}(应收敛回 ${PHY.HSPD})`);
}
// B:松手靠惯性滑出到普通地面 —— 地面摩擦收住,但不能一帧刹死
{
const { off } = run('='.repeat(34), 'B 冰面 -> 普通地面(第30帧松开方向键)', 30);
const v0 = off[0].vx, v5 = off[5]?.vx ?? 0;
if (v5 <= 0) err(`B 离冰 5 帧内就刹死了(v0=${v0} v5=${v5})`);
if (v5 > v0) err(`B 松手后速度反而上升`);
}
// C:滑出冰块边缘掉下去 —— 空中必须几乎不衰减,能一路飞出去
{
const { off } = run('.'.repeat(34), 'C 冰面 -> 悬空(第30帧松开后滑出边缘)', 30);
const air = off.filter(r => !r.g);
if (air.length < 5) { err('C 没掉下去(场景构造失败)'); }
else {
const k = Math.min(10, air.length - 1);
const v0 = air[0].vx, vk = air[k].vx;
if (vk < v0 - 0.5) err(`C 空中惯性衰减过快:${v0} -> ${vk}(${k} 帧)`);
else console.log(` ✓ 空中 ${k} 帧后仍保留 vx=${vk}(起 ${v0})`);
}
}
// D:冰面上滑行中起跳 —— 空中保持滑行速度
{
const { off } = run('.'.repeat(34), 'D 冰面滑行中起跳(第30帧松开 + 第34帧跳)', 30, 34);
const v0 = off[0]?.vx ?? 0, v12 = off[12]?.vx ?? 0;
if (v0 < 2) err(`D 起跳时速度太小 vx=${v0}`);
if (v12 < v0 - 0.6) err(`D 跳跃后空中丢失惯性:${v0} -> ${v12}`);
else console.log(` ✓ 起跳后 12 帧仍保留 vx=${v12}(起 ${v0})`);
}
console.log(fail ? `\n❌ ${fail} 项失败` : '\n✅ 冰面惯性全部通过');
process.exit(fail ? 1 : 0);