PAYATHON 2026

JavaScript 中关卡导入导出快捷键为何无法正常工作?

数据小陈

结论

仅凭这段代码无法确定具体故障原因,因为没有提供异常表现或控制台报错。可以先检查两处:btoa() 无法直接编码含中文等非 Latin-1 字符的 JSON 字符串;快捷键监听器则会在焦点位于输入框时触发。还要注意,Base64 只是编码,不具备加密功能。

title 和 author 的值来自 prompt()。如果输入中文等字符,JSON.stringify(data) 会包含这些字符,直接传给 btoa() 可能触发 InvalidCharacterError,导致导出中断。可以查看浏览器控制台是否有这条错误。

用 UTF-8 处理 Base64

先添加 UTF-8 编码和解码函数:

function encodeBase64Utf8(text) {
  const bytes = new TextEncoder().encode(text);
  let binary = '';

  for (const byte of bytes) {
    binary += String.fromCharCode(byte);
  }

  return btoa(binary);
}

function decodeBase64Utf8(code) {
  const binary = atob(code.trim());
  const bytes = Uint8Array.from(binary, char => char.charCodeAt(0));

  return new TextDecoder().decode(bytes);
}

在 exportLevel() 中,将:

const code = btoa(JSON.stringify(data));

替换为:

const code = encodeBase64Utf8(JSON.stringify(data));

在 loadFromCode() 中,将:

const data = JSON.parse(atob(code));

替换为:

const data = JSON.parse(decodeBase64Utf8(code));

检查导入数据后再更新网格

当前代码会直接读取 data.grid[y][x]。如果粘贴的代码无效、网格行列数与当前 gridSize 不一致,或数据结构不完整,循环中就可能报错。catch 虽然会显示 Invalid save code!,但如果异常发生前已有部分网格更新,画面状态可能不完整。

可以先检查数据结构,通过后再更新网格:

function loadFromCode(code) {
  try {
    const data = JSON.parse(decodeBase64Utf8(code));

    if (!data || !Array.isArray(data.grid) || data.grid.length !== gridSize) {
      throw new Error('Invalid grid height');
    }

    const hasValidRows = data.grid.every(
      row => Array.isArray(row) && row.length === gridSize
    );
    if (!hasValidRows) {
      throw new Error('Invalid grid width');
    }

    for (let y = 0; y < gridSize; y++) {
      for (let x = 0; x < gridSize; x++) {
        grid[y][x].type = data.grid[y][x];
      }
    }

    levelTitle = data.title || 'Untitled Level';
    levelAuthor = data.author || 'Unknown';
    player = null;
    gameStarted = false;
    renderGrid();
    updateLevelMeta();
    alert(`Level loaded!\n${levelTitle} By: ${levelAuthor}`);
  } catch (error) {
    console.error('Unable to load level:', error);
    alert('Invalid save code!');
  }
}

这里假设每个关卡的尺寸都是 gridSize × gridSize。如果游戏支持不同尺寸的关卡,需要根据实际数据格式调整校验条件。

避免在输入文字时触发快捷键

当前的 keydown 监听器在页面任何位置都会生效。用户输入标题、作者名或其他表单内容时,也可能触发快捷键。可以忽略输入控件中的按键,并跳过按住按键产生的重复事件:

document.addEventListener('keydown', (e) => {
  const target = e.target;
  if (
    target instanceof HTMLElement &&
    (target.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(target.tagName))
  ) {
    return;
  }

  if (e.repeat) return;

  const key = e.key.toLowerCase();
  if (key === 'o') {
    exportLevel();
  } else if (key === 'p') {
    const code = prompt('Paste your save code:');
    if (code) loadFromCode(code);
  }
});

排查顺序

  1. 打开浏览器开发者工具,查看按下 O 或 P 时是否出现异常。也可以在分支开头临时添加 console.log('O pressed'),确认监听器是否执行。
  2. 如果输入中文后导出失败,先换用 UTF-8 Base64 编解码函数。
  3. 如果快捷键只在页面的部分区域失效,检查焦点是否位于输入框或可编辑元素中。
  4. 如果出现 Invalid save code!,开发时查看 console.error 的输出,以区分 Base64 解码、JSON 解析和网格结构校验错误。

Base64 不提供保密或防篡改能力。如果需要防止关卡代码被查看或修改,需要另行设计机制。仅凭当前代码无法判断是否有这类需求。

备注:内容仅供参考。