fix: 优化跳跃和下落手感
This commit is contained in:
1 parent
ada64fe4be
commit
afe2f64b9b
25 files changed
+1197
-188
No files matched your search
+91
-2
@@ -30,6 +30,7 @@ class El {
|
||||
remove() { if (this.parent) { const i = this.parent.children.indexOf(this); if (i >= 0) this.parent.children.splice(i, 1); } }
|
||||
querySelector() { return new El(); }
|
||||
querySelectorAll(sel) { return sel === '.tc-btn' ? this.children.filter(c => c._classes.has('tc-btn')) : []; }
|
||||
closest(sel) { const cls = sel.replace(/^\./, ''); let n = this; while (n) { if (n._classes && n._classes.has(cls)) return n; n = n.parent; } return null; }
|
||||
addEventListener(type, fn) { (this._ev[type] ||= []).push(fn); }
|
||||
removeEventListener() { }
|
||||
dispatch(type, ev) { (this._ev[type] || []).forEach(fn => fn(ev)); }
|
||||
@@ -40,6 +41,7 @@ class El {
|
||||
|
||||
const byId = new Map();
|
||||
const touchMap = new Map();
|
||||
const docListeners = {};
|
||||
globalThis.localStorage = {
|
||||
_d: {},
|
||||
getItem(k) { return k in this._d ? this._d[k] : null; },
|
||||
@@ -55,8 +57,13 @@ globalThis.document = {
|
||||
return new El();
|
||||
},
|
||||
querySelectorAll() { return []; },
|
||||
addEventListener(type, fn) { (docListeners[type] ||= []).push(fn); },
|
||||
removeEventListener() { },
|
||||
dispatch(type, ev) { (docListeners[type] || []).forEach(fn => fn(ev)); },
|
||||
};
|
||||
globalThis.window = { addEventListener: noop, removeEventListener: noop };
|
||||
globalThis.addEventListener = (t, fn) => { (docListeners[t] ||= []).push(fn); };
|
||||
globalThis.removeEventListener = noop;
|
||||
globalThis.confirm = () => true;
|
||||
|
||||
let fail = 0;
|
||||
@@ -64,9 +71,15 @@ const ok = (c, m) => { console.log((c ? ' ✔ ' : ' ✗ ') + m); if (!c) fail+
|
||||
|
||||
const { Save } = await import('../js/save.js');
|
||||
const { Settings } = await import('../js/settings.js');
|
||||
const { applyBindings, defaultBindings } = await import('../js/input.js');
|
||||
|
||||
console.log('=== 初始化 / 默认布局 ===');
|
||||
Settings.init();
|
||||
console.log('=== input 键位 API ===');
|
||||
ok(defaultBindings().left.includes('ArrowLeft'), 'defaultBindings 含出厂左移键');
|
||||
applyBindings({ left: ['KeyA'], jump: ['Space'] });
|
||||
ok(true, 'applyBindings 调用不报错');
|
||||
|
||||
console.log('=== 初始化 / 默认布局(PC 模式)===');
|
||||
Settings.init(false);
|
||||
const s0 = Save.data.settings;
|
||||
ok(!!s0 && !!s0.touch, '存档含 settings.touch');
|
||||
const keys = ['left', 'right', 'jump', 'shoot', 'down', 'pause', 'suicide'];
|
||||
@@ -130,5 +143,81 @@ ok(wt.left.size > size0 && wt.left.size <= 160, `缩放:尺寸随距离放大
|
||||
handle.dispatch('pointerup', ev(handle, 176, 566));
|
||||
ok(wt.left.cx === cx0 && wt.left.cy === cy0, '缩放:不改变按钮位置');
|
||||
|
||||
console.log('=== 设备模式:PC 显示键盘映射,移动端显示手柄 ===');
|
||||
Settings.init(true); // isTouch=true → 移动端
|
||||
Settings.enter();
|
||||
ok(byId.get('set-keys-row')._classes.has('hidden'), '移动端:键盘映射面板隐藏');
|
||||
ok(!byId.get('set-touch-row')._classes.has('hidden'), '移动端:手柄面板显示');
|
||||
Settings.init(false); // PC
|
||||
Settings.enter();
|
||||
ok(!byId.get('set-keys-row')._classes.has('hidden'), 'PC:键盘映射面板显示');
|
||||
ok(byId.get('set-touch-row')._classes.has('hidden'), 'PC:手柄面板隐藏');
|
||||
|
||||
console.log('=== 键盘重绑:点击按键 → 按新键 ===');
|
||||
Settings.enter();
|
||||
const keysBox = byId.get('set-keys');
|
||||
const firstRow = keysBox.children[0]; // 左移行
|
||||
const kkeys = firstRow.children[1]; // kkeys 容器
|
||||
const chip = kkeys.children[0]; // 第一个按键 chip(left[0])
|
||||
ok(chip.dataset.kact === 'left' && String(chip.dataset.kidx) === '0', '首行首键为 left[0]');
|
||||
keysBox.dispatch('click', { target: chip }); // 进入重绑监听
|
||||
document.dispatch('keydown', { code: 'KeyB', preventDefault: noop }); // KeyB 未被任何动作占用
|
||||
ok(Settings._state().keys.left[0] === 'KeyB', '重绑:left[0] 变为空闲键 KeyB');
|
||||
// 添加第二个键
|
||||
const before = Settings._state().keys.left.length;
|
||||
const addBtn = kkeys.children[kkeys.children.length - 1]; // +添加
|
||||
keysBox.dispatch('click', { target: addBtn });
|
||||
document.dispatch('keydown', { code: 'KeyF', preventDefault: noop }); // KeyF 空闲
|
||||
ok(Settings._state().keys.left.length === before + 1 && Settings._state().keys.left.includes('KeyF'), '添加:left 多绑定了一个键 KeyF');
|
||||
// 删除第一个键(即刚重绑的 KeyB)
|
||||
const chip1 = kkeys.children[0];
|
||||
const rm = chip1.children.find(c => c._classes.has('krm'));
|
||||
keysBox.dispatch('click', { target: rm });
|
||||
ok(!Settings._state().keys.left.includes('KeyB') && Settings._state().keys.left.includes('KeyF'), '删除:left[0] 被移除(KeyB 消失,KeyF 仍在)');
|
||||
// 确定后落盘
|
||||
Settings.confirm();
|
||||
ok(Save.data.settings.keys.left.includes('KeyF'), '确定:键位变更已保存到存档');
|
||||
|
||||
console.log('=== 键盘冲突检测 ===');
|
||||
Settings.reset(); // 出厂:left=[ArrowLeft,KeyA]
|
||||
Settings.enter();
|
||||
const kb = byId.get('set-keys');
|
||||
const jumpRow = kb.children[3]; // 跳跃行(KEY_ACTIONS 第 4 项,down 已移除)
|
||||
const jumpKeys = jumpRow.children[1];
|
||||
const jumpChip = jumpKeys.children[0];
|
||||
ok(jumpChip.dataset.kact === 'jump', '找到跳跃行的按键');
|
||||
kb.dispatch('click', { target: jumpChip }); // 监听跳跃键
|
||||
document.dispatch('keydown', { code: 'KeyA', preventDefault: noop }); // KeyA 已被左移占用
|
||||
const st2 = Settings._state();
|
||||
ok(!st2.keys.jump.includes('KeyA'), '冲突:jump 不会绑定到被【左移】占用的 KeyA');
|
||||
ok(Settings._conflict() && Settings._conflict().act === 'jump' && Settings._conflict().label === '左移', '冲突:记录到被【左移】占用');
|
||||
ok(kb.children[0] && kb.children[0]._classes.has('kwarb'), '冲突:面板顶部显示警告横幅');
|
||||
document.dispatch('keydown', { code: 'KeyK', preventDefault: noop }); // 换空闲键
|
||||
ok(st2.keys.jump[0] === 'KeyK', '冲突解除:改用空闲键 KeyK 成功');
|
||||
ok(Settings._conflict() === null, '冲突:解除后警告清空');
|
||||
// 同一动作内重复键也算冲突(jump 已含 KeyK,再绑到第二个槽应被拒)
|
||||
kb.dispatch('click', { target: jumpKeys.children[jumpKeys.children.length - 1] }); // +添加
|
||||
document.dispatch('keydown', { code: 'KeyK', preventDefault: noop });
|
||||
ok(st2.keys.jump.filter(c => c === 'KeyK').length === 1, '冲突:同一动作内重复键不重复写入');
|
||||
// Esc 取消监听
|
||||
kb.dispatch('click', { target: jumpChip });
|
||||
document.dispatch('keydown', { code: 'Escape', preventDefault: noop });
|
||||
ok(Settings._listening() === null && Settings._conflict() === null, 'Esc:退出监听且无残留警告');
|
||||
// 系统动作(菜单 KeyQ)也算冲突
|
||||
kb.dispatch('click', { target: jumpChip });
|
||||
document.dispatch('keydown', { code: 'KeyQ', preventDefault: noop });
|
||||
ok(Settings._conflict() && Settings._conflict().label === '菜单', '冲突:KeyQ 已被系统「菜单」占用');
|
||||
|
||||
console.log('=== 恢复默认:键位回到出厂 ===');
|
||||
Settings.reset();
|
||||
ok(Settings._state().keys.left.includes('ArrowLeft'), '恢复默认:left 含出厂 ArrowLeft');
|
||||
ok(Settings._state().keys.jump.includes('ShiftLeft'), '恢复默认:jump 含出厂 ShiftLeft');
|
||||
|
||||
console.log('=== 回归:main.js 中「确定」应返回上一页 ===');
|
||||
const mainSrc = (await import('node:fs')).readFileSync(new URL('../js/main.js', import.meta.url), 'utf8');
|
||||
ok(/settings:\s*\(\)\s*=>\s*\{\s*settingsFrom\s*=\s*UI\.cur/.test(mainSrc), '进入设置前记录来源界面');
|
||||
ok(/'set-ok':\s*\(\)\s*=>\s*\{\s*Settings\.confirm\(\);\s*UI\.show\(settingsFrom\)/.test(mainSrc), '确定后 UI.show(settingsFrom) 返回上一页');
|
||||
ok(/Settings\.init\(useTouch\)/.test(mainSrc), 'Settings.init 传入设备检测结果');
|
||||
|
||||
console.log(fail === 0 ? '\n✅ settings 全部断言通过' : `\n❌ 失败 ${fail} 项`);
|
||||
process.exit(fail === 0 ? 0 : 1);
|
||||
Reference in new issue
Block a user