diff --git a/game/橘子/script.js b/game/橘子/script.js index 42ae5e3..e7efbd9 100644 --- a/game/橘子/script.js +++ b/game/橘子/script.js @@ -199,25 +199,45 @@ async function loadKNNModel(jsonUrl = null, binUrl = null) { } } else { - const inputJson = document.createElement('input'); - inputJson.type = 'file'; - inputJson.accept = '.json'; - inputJson.multiple = false; - - showStatus(MODEL_STATUS, 'info', '请先选择 KNN 模型配置文件 (.json)...'); - + // 这是用户手动加载模型文件的分支,需要修改这里 + const inputFiles = document.createElement('input'); + inputFiles.type = 'file'; + // 允许同时选择多个文件 + inputFiles.accept = '.json,.bin'; // 限制文件类型 + inputFiles.multiple = true; // 关键:允许选择多个文件 + + showStatus(MODEL_STATUS, 'info', '请选择 KNN 模型配置文件 (.json) 以及其对应的权重文件 (.bin)...'); + await new Promise((resolve, reject) => { - inputJson.onchange = async (e) => { - const jsonFile = e.target.files[0]; - if (!jsonFile) { - showStatus(MODEL_STATUS, 'info', '未选择 .json 文件。'); + inputFiles.onchange = async (e) => { + const files = e.target.files; + if (files.length === 0) { + showStatus(MODEL_STATUS, 'info', '未选择文件。'); updateModelUI(false); - return reject(new Error('No JSON file selected.')); + return reject(new Error('No files selected.')); } - + + let jsonFile = null; + let binFile = null; + + // 遍历所有选择的文件,找到 .json 和 .bin + for (const file of files) { + if (file.name.endsWith('.json')) { + jsonFile = file; + } else if (file.name.endsWith('.bin')) { + binFile = file; + } + } + + if (!jsonFile) { + showStatus(MODEL_STATUS, 'error', '请选择一个 .json 模型配置文件。'); + updateModelUI(false); + return reject(new Error('No JSON file found.')); + } + showStatus(MODEL_STATUS, 'info', `正在解析 ${jsonFile.name}...`); modelName = jsonFile.name; - + try { const reader = new FileReader(); const jsonText = await new Promise((res, rej) => { @@ -226,61 +246,53 @@ async function loadKNNModel(jsonUrl = null, binUrl = null) { reader.readAsText(jsonFile); }); modelData = JSON.parse(jsonText); - + + // 兼容旧的单文件JSON格式 if (!modelData.dataFile) { console.warn('模型JSON文件不包含 "dataFile" 字段,尝试以旧的单文件JSON格式加载。'); await loadSingleJsonModel(modelData); - return resolve(); + return resolve(); // 单文件模型加载成功后直接返回 } - + } catch (error) { showStatus(MODEL_STATUS, 'error', `解析 .json 文件失败: ${error.message}`); console.error('解析 .json 失败:', error); updateModelUI(false); return reject(error); } - - const inputBin = document.createElement('input'); - inputBin.type = 'file'; - inputBin.accept = '.bin'; - inputBin.multiple = false; - - showStatus(MODEL_STATUS, 'info', `已加载 .json 文件。请选择对应的权重文件 "${modelData.dataFile}" (.bin)...`); - - inputBin.onchange = async (eBin) => { - const binFile = eBin.target.files[0]; - if (!binFile) { - showStatus(MODEL_STATUS, 'info', '未选择 .bin 文件。'); - updateModelUI(false); - return reject(new Error('No BIN file selected.')); - } - - if (binFile.name !== modelData.dataFile) { - showStatus(MODEL_STATUS, 'error', `选择的 .bin 文件名 "${binFile.name}" 与 .json 中定义的 "${modelData.dataFile}" 不匹配!请选择正确的文件。`); - updateModelUI(false); - return reject(new Error('BIN file name mismatch.')); - } - - showStatus(MODEL_STATUS, 'info', `正在读取 ${binFile.name} (二进制权重文件)...`); - try { - const reader = new FileReader(); - const arrayBuffer = await new Promise((res, rej) => { - reader.onload = () => res(reader.result); - reader.onerror = () => rej(reader.error); - reader.readAsArrayBuffer(binFile); - }); - binData = new Float32Array(arrayBuffer); - resolve(); - } catch (error) { - showStatus(MODEL_STATUS, 'error', `读取 .bin 文件失败: ${error.message}`); - console.error('读取 .bin 失败:', error); - updateModelUI(false); - return reject(error); - } - }; - inputBin.click(); + + // 如果是多文件模式,确保也选择了 bin 文件 + if (!binFile) { + showStatus(MODEL_STATUS, 'error', `请同时选择与 ${jsonFile.name} 对应的 .bin 权重文件 (${modelData.dataFile})。`); + updateModelUI(false); + return reject(new Error('No BIN file found.')); + } + + // 检查 bin 文件名是否匹配 + if (binFile.name !== modelData.dataFile) { + showStatus(MODEL_STATUS, 'error', `选择的 .bin 文件名 "${binFile.name}" 与 .json 中定义的 "${modelData.dataFile}" 不匹配!请选择正确的文件。`); + updateModelUI(false); + return reject(new Error('BIN file name mismatch.')); + } + + showStatus(MODEL_STATUS, 'info', `正在读取 ${binFile.name} (二进制权重文件)...`); + try { + const reader = new FileReader(); + const arrayBuffer = await new Promise((res, rej) => { + reader.onload = () => res(reader.result); + reader.onerror = () => rej(reader.error); + reader.readAsArrayBuffer(binFile); + }); + binData = new Float32Array(arrayBuffer); + resolve(); // 所有文件都已读取成功 + } catch (error) { + showStatus(MODEL_STATUS, 'error', `读取 .bin 文件失败: ${error.message}`); + console.error('读取 .bin 失败:', error); + updateModelUI(false); + return reject(error); + } }; - inputJson.click(); + inputFiles.click(); // 触发一次文件选择 }); } diff --git a/game/贪吃蛇/snake_game.html b/game/贪吃蛇/snake_game.html index 7ce2013..cb80cd0 100644 --- a/game/贪吃蛇/snake_game.html +++ b/game/贪吃蛇/snake_game.html @@ -634,6 +634,41 @@ } async function startPoseDetectionLoop() { + // 手动调整方向 + window.addEventListener('keydown', (event) => { + let direction = null; + + // 检查按下的键,并映射到对应的方向 + switch (event.key.toUpperCase()) { // 转换为大写以确保大小写不敏感 + case 'W': + direction = 'UP'; + break; + case 'S': + direction = 'DOWN'; + break; + case 'A': + direction = 'LEFT'; + break; + case 'D': + direction = 'RIGHT'; + break; + default: + // 如果按下的不是W S A D,则不执行任何操作 + return; + } + + // 如果方向被设置(即按下了W S A D之一) + if (direction) { + // 阻止某些键(如A和D)可能导致的浏览器默认行为 + // 这在游戏场景中非常重要,以确保键只用于游戏控制 + event.preventDefault(); + + // 调用外部函数来改变蛇的方向 + changeSnakeDirection(direction); + } + }); + + if (!isPoseDetectionReady) return; async function detectAndPredict() {