523 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
		
		
			
		
	
	
			523 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
|  | <!DOCTYPE html> | |||
|  | <html lang="zh-CN"> | |||
|  |   <head> | |||
|  |     <meta charset="UTF-8" /> | |||
|  |     <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |||
|  |     <title>示例游戏 - 荣光课堂</title> | |||
|  |     <style> | |||
|  |       body { | |||
|  |         font-family: Arial, sans-serif; | |||
|  |         text-align: center; | |||
|  |         padding: 20px; | |||
|  |         background-color: #f0f0f0; | |||
|  |       } | |||
|  |       .game-container { | |||
|  |         max-width: 600px; | |||
|  |         margin: 0 auto; | |||
|  |         background: white; | |||
|  |         padding: 30px; | |||
|  |         border-radius: 10px; | |||
|  |         box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); | |||
|  |       } | |||
|  |       .question { | |||
|  |         font-size: 24px; | |||
|  |         margin: 20px 0; | |||
|  |         color: #333; | |||
|  |       } | |||
|  |       .options { | |||
|  |         display: flex; | |||
|  |         justify-content: center; | |||
|  |         gap: 20px; | |||
|  |         margin: 30px 0; | |||
|  |       } | |||
|  |       .option-btn { | |||
|  |         padding: 15px 30px; | |||
|  |         font-size: 18px; | |||
|  |         border: none; | |||
|  |         border-radius: 5px; | |||
|  |         cursor: pointer; | |||
|  |         background-color: #007bff; | |||
|  |         color: white; | |||
|  |         transition: background-color 0.3s; | |||
|  |       } | |||
|  |       .option-btn:hover { | |||
|  |         background-color: #0056b3; | |||
|  |       } | |||
|  |       .option-btn.correct { | |||
|  |         background-color: #28a745; | |||
|  |       } | |||
|  |       .option-btn.incorrect { | |||
|  |         background-color: #dc3545; | |||
|  |       } | |||
|  |       .result { | |||
|  |         font-size: 20px; | |||
|  |         margin: 20px 0; | |||
|  |         padding: 15px; | |||
|  |         border-radius: 5px; | |||
|  |       } | |||
|  |       .result.correct { | |||
|  |         background-color: #d4edda; | |||
|  |         color: #155724; | |||
|  |       } | |||
|  |       .result.incorrect { | |||
|  |         background-color: #f8d7da; | |||
|  |         color: #721c24; | |||
|  |       } | |||
|  |       .score { | |||
|  |         font-size: 18px; | |||
|  |         margin: 20px 0; | |||
|  |         color: #666; | |||
|  |       } | |||
|  |       .restart-btn { | |||
|  |         padding: 10px 20px; | |||
|  |         font-size: 16px; | |||
|  |         border: none; | |||
|  |         border-radius: 5px; | |||
|  |         cursor: pointer; | |||
|  |         background-color: #6c757d; | |||
|  |         color: white; | |||
|  |         margin-top: 20px; | |||
|  |       } | |||
|  |       .restart-btn:hover { | |||
|  |         background-color: #545b62; | |||
|  |       } | |||
|  | 
 | |||
|  |       /* 体感交互视频样式 */ | |||
|  |       .body-sensation-container { | |||
|  |         position: fixed; | |||
|  |         bottom: 20px; | |||
|  |         left: 50%; | |||
|  |         transform: translateX(-50%); | |||
|  |         background: rgba(255, 255, 255, 0.95); | |||
|  |         border-radius: 15px; | |||
|  |         padding: 20px; | |||
|  |         box-shadow: 0 5px 20px rgba(0, 0, 0, 0.3); | |||
|  |         z-index: 1000; | |||
|  |         max-width: 600px; | |||
|  |       } | |||
|  |       .body-video-feed { | |||
|  |         position: relative; | |||
|  |         width: 500px; | |||
|  |         height: 350px; | |||
|  |         border-radius: 10px; | |||
|  |         overflow: hidden; | |||
|  |         margin-bottom: 15px; | |||
|  |       } | |||
|  |       .body-video-feed img { | |||
|  |         width: 100%; | |||
|  |         height: 100%; | |||
|  |         object-fit: cover; | |||
|  |       } | |||
|  |       .body-status-overlay { | |||
|  |         position: absolute; | |||
|  |         top: 15px; | |||
|  |         left: 15px; | |||
|  |         right: 15px; | |||
|  |         background: rgba(0, 0, 0, 0.7); | |||
|  |         color: white; | |||
|  |         padding: 8px 15px; | |||
|  |         border-radius: 8px; | |||
|  |         font-size: 14px; | |||
|  |         line-height: 1.3; | |||
|  |       } | |||
|  |       .body-instruction { | |||
|  |         text-align: center; | |||
|  |         font-size: 18px; | |||
|  |         color: #333; | |||
|  |         font-weight: 500; | |||
|  |         padding: 8px; | |||
|  |       } | |||
|  |       /* 体感交互容器样式(用于directBodySensation.js) */ | |||
|  |       .box-body { | |||
|  |         position: fixed; | |||
|  |         top: 50%; | |||
|  |         left: 50%; | |||
|  |         transform: translate(-50%, -50%); | |||
|  |         background: rgba(255, 255, 255, 0.95); | |||
|  |         border-radius: 15px; | |||
|  |         padding: 20px; | |||
|  |         box-shadow: 0 5px 20px rgba(0, 0, 0, 0.3); | |||
|  |         z-index: 2000; | |||
|  |         max-width: 500px; | |||
|  |         text-align: center; | |||
|  |       } | |||
|  |       .box-body img { | |||
|  |         max-width: 100%; | |||
|  |         height: auto; | |||
|  |         border-radius: 10px; | |||
|  |         margin: 10px 0; | |||
|  |       } | |||
|  |       .box-body div { | |||
|  |         margin: 10px 0; | |||
|  |         padding: 5px; | |||
|  |       } | |||
|  |     </style> | |||
|  |   </head> | |||
|  |   <body> | |||
|  |     <div class="game-container"> | |||
|  |       <h1>数学小测验</h1> | |||
|  |       <div class="question" id="question">2 + 3 = ?</div> | |||
|  |       <div class="options" id="options"> | |||
|  |         <button class="option-btn" onclick="selectOption(4)">4</button> | |||
|  |         <button class="option-btn" onclick="selectOption(5)">5</button> | |||
|  |         <button class="option-btn" onclick="selectOption(6)">6</button> | |||
|  |       </div> | |||
|  |       <div class="result" id="result" style="display: none"></div> | |||
|  |       <div class="score" id="score">得分: 0</div> | |||
|  |       <button class="restart-btn" onclick="restartGame()" style="display: none"> | |||
|  |         重新开始 | |||
|  |       </button> | |||
|  |     </div> | |||
|  | 
 | |||
|  |     <!-- 体感交互视频容器 --> | |||
|  |     <div | |||
|  |       class="body-sensation-container" | |||
|  |       id="bodySensationContainer" | |||
|  |       style="display: none" | |||
|  |     > | |||
|  |       <div class="body-video-feed"> | |||
|  |         <img id="bodyVideoFeed" src="" alt="体感检测视频" /> | |||
|  |         <div class="body-status-overlay"> | |||
|  |           <div id="bodyConnectionStatus">未连接到服务器</div> | |||
|  |           <div id="bodyHandStatus">等待检测...</div> | |||
|  |         </div> | |||
|  |       </div> | |||
|  |       <div class="body-instruction" id="bodyInstruction"> | |||
|  |         请举起左手或右手选择答案 | |||
|  |       </div> | |||
|  |     </div> | |||
|  | 
 | |||
|  |     <!-- 体感交互容器(用于directBodySensation.js) --> | |||
|  |     <div class="box-body" style="display: none"> | |||
|  |       <div id="bodyConnectionStatus">未连接到服务器</div> | |||
|  |       <img id="bodyVideoFeed" src="" alt="体感检测视频" /> | |||
|  |       <div id="bodyHandStatus">等待检测...</div> | |||
|  |     </div> | |||
|  | 
 | |||
|  |     <!-- 引入后端集成脚本 --> | |||
|  |     <script src="../../js/apiService.js"></script> | |||
|  |     <script src="../../js/dataManager.js"></script> | |||
|  |     <script src="../../js/userManager.js"></script> | |||
|  |     <script src="../../js/accessTracker.js"></script> | |||
|  |     <script src="../../js/gameTracker.js"></script> | |||
|  |     <script src="../../js/gameDataLogger.js"></script> | |||
|  |     <script src="../../js/virtualTeacher.js"></script> | |||
|  |     <!-- 引入体感交互脚本 --> | |||
|  |     <script src="../../js/directBodySensation.js"></script> | |||
|  | 
 | |||
|  |     <script> | |||
|  |       let currentQuestion = 0; | |||
|  |       let score = 0; | |||
|  |       let gameStarted = false; | |||
|  |       let gameId = 1; // 示例游戏ID,实际应该从数据库获取 | |||
|  | 
 | |||
|  |       const questions = [ | |||
|  |         { question: "2 + 3 = ?", options: [4, 5, 6], correct: 5 }, | |||
|  |         { question: "7 - 2 = ?", options: [4, 5, 6], correct: 5 }, | |||
|  |         { question: "3 × 2 = ?", options: [5, 6, 7], correct: 6 }, | |||
|  |         { question: "8 ÷ 2 = ?", options: [3, 4, 5], correct: 4 }, | |||
|  |         { question: "1 + 1 = ?", options: [1, 2, 3], correct: 2 }, | |||
|  |       ]; | |||
|  | 
 | |||
|  |       // 初始化游戏 | |||
|  |       function initGame() { | |||
|  |         // 检查用户是否已登录 | |||
|  |         if (!window.userManager.isUserLoggedIn()) { | |||
|  |           alert("请先登录后再开始游戏!"); | |||
|  |           return; | |||
|  |         } | |||
|  | 
 | |||
|  |         // 重置虚拟老师统计 | |||
|  |         if (window.virtualTeacher) { | |||
|  |           window.virtualTeacher.resetStats(); | |||
|  |           window.virtualTeacher.gameStart("示例游戏"); | |||
|  |         } | |||
|  | 
 | |||
|  |         // 开始游戏跟踪 | |||
|  |         if (window.gameTracker) { | |||
|  |           window.gameTracker.startGame(gameId, { | |||
|  |             gameType: "math_quiz", | |||
|  |             totalQuestions: questions.length, | |||
|  |           }); | |||
|  |           gameStarted = true; | |||
|  |         } | |||
|  | 
 | |||
|  |         currentQuestion = 0; | |||
|  |         score = 0; | |||
|  |         showQuestion(); | |||
|  |       } | |||
|  | 
 | |||
|  |       // 显示问题 | |||
|  |       function showQuestion() { | |||
|  |         if (currentQuestion >= questions.length) { | |||
|  |           endGame(); | |||
|  |           return; | |||
|  |         } | |||
|  | 
 | |||
|  |         const q = questions[currentQuestion]; | |||
|  |         document.getElementById("question").textContent = q.question; | |||
|  | 
 | |||
|  |         // 虚拟老师朗读题目 | |||
|  |         if (window.virtualTeacher) { | |||
|  |           const questionText = `第${currentQuestion + 1}题:${ | |||
|  |             q.question | |||
|  |           },选项有:${q.options.join("、")}`; | |||
|  |           window.virtualTeacher.readQuestion(questionText); | |||
|  |         } | |||
|  | 
 | |||
|  |         const optionsDiv = document.getElementById("options"); | |||
|  |         optionsDiv.innerHTML = ""; | |||
|  | 
 | |||
|  |         q.options.forEach((option) => { | |||
|  |           const btn = document.createElement("button"); | |||
|  |           btn.className = "option-btn"; | |||
|  |           btn.textContent = option; | |||
|  |           btn.onclick = () => selectOption(option); | |||
|  |           optionsDiv.appendChild(btn); | |||
|  |         }); | |||
|  | 
 | |||
|  |         // 显示体感交互视频 | |||
|  |         showBodyInteraction(); | |||
|  | 
 | |||
|  |         // 更新体感交互提示文本 | |||
|  |         if (q.options.length >= 2) { | |||
|  |           updateBodyInstruction( | |||
|  |             `请举起左手选择${q.options[0]}或举起右手选择${q.options[1]}` | |||
|  |           ); | |||
|  |         } else if (q.options.length === 1) { | |||
|  |           updateBodyInstruction(`请举起任意一只手选择:${q.options[0]}`); | |||
|  |         } else { | |||
|  |           updateBodyInstruction("请举起左手或右手选择答案"); | |||
|  |         } | |||
|  | 
 | |||
|  |         document.getElementById("result").style.display = "none"; | |||
|  |         document.getElementById("restart-btn").style.display = "none"; | |||
|  |       } | |||
|  | 
 | |||
|  |       // 体感交互控制函数 | |||
|  |       function showBodyInteraction() { | |||
|  |         const bodySensationContainer = document.getElementById( | |||
|  |           "bodySensationContainer" | |||
|  |         ); | |||
|  |         if (bodySensationContainer) { | |||
|  |           bodySensationContainer.style.display = "block"; | |||
|  |         } | |||
|  |         // 启动体感交互系统 | |||
|  |         if (typeof showBody === "function") { | |||
|  |           showBody(); | |||
|  |         } | |||
|  |       } | |||
|  | 
 | |||
|  |       function hideBodyInteraction() { | |||
|  |         const bodySensationContainer = document.getElementById( | |||
|  |           "bodySensationContainer" | |||
|  |         ); | |||
|  |         if (bodySensationContainer) { | |||
|  |           bodySensationContainer.style.display = "none"; | |||
|  |         } | |||
|  |       } | |||
|  | 
 | |||
|  |       function updateBodyInstruction(text) { | |||
|  |         const bodyInstruction = document.getElementById("bodyInstruction"); | |||
|  |         if (bodyInstruction) { | |||
|  |           bodyInstruction.textContent = text; | |||
|  |         } | |||
|  |       } | |||
|  | 
 | |||
|  |       // 选择答案 | |||
|  |       function selectOption(selected) { | |||
|  |         if (!gameStarted) return; | |||
|  | 
 | |||
|  |         // 隐藏体感交互按钮 | |||
|  |         hideBodyInteraction(); | |||
|  | 
 | |||
|  |         // 记录游戏尝试 | |||
|  |         if (window.gameTracker) { | |||
|  |           window.gameTracker.recordAttempt(); | |||
|  |         } | |||
|  | 
 | |||
|  |         const q = questions[currentQuestion]; | |||
|  |         const isCorrect = selected === q.correct; | |||
|  | 
 | |||
|  |         // 虚拟老师反馈 | |||
|  |         if (window.virtualTeacher) { | |||
|  |           // 立即停止当前语音 | |||
|  |           window.virtualTeacher.stopAllSpeech(); | |||
|  | 
 | |||
|  |           // 延迟一点再播放反馈语音 | |||
|  |           setTimeout(() => { | |||
|  |             if (isCorrect) { | |||
|  |               window.virtualTeacher.recordCorrect(); | |||
|  |             } else { | |||
|  |               window.virtualTeacher.recordIncorrect(); | |||
|  |             } | |||
|  |           }, 300); | |||
|  |         } | |||
|  | 
 | |||
|  |         // 详细数据打印 | |||
|  |         console.log("🎯 示例游戏 - 用户选择记录"); | |||
|  |         console.log("📊 问题信息:", { | |||
|  |           问题编号: currentQuestion + 1, | |||
|  |           问题内容: q.question, | |||
|  |           选项: q.options, | |||
|  |           正确答案: q.correct, | |||
|  |           用户选择: selected, | |||
|  |           是否正确: isCorrect ? "✅ 正确" : "❌ 错误", | |||
|  |         }); | |||
|  |         console.log("📈 游戏进度:", { | |||
|  |           当前得分: score, | |||
|  |           当前问题: currentQuestion + 1, | |||
|  |           总问题数: questions.length, | |||
|  |           完成进度: `${( | |||
|  |             ((currentQuestion + 1) / questions.length) * | |||
|  |             100 | |||
|  |           ).toFixed(1)}%`, | |||
|  |         }); | |||
|  |         console.log("⏰ 时间信息:", { | |||
|  |           选择时间: new Date().toLocaleString("zh-CN"), | |||
|  |           游戏时长: window.gameTracker | |||
|  |             ? window.gameTracker.getCurrentPlayTime() + "秒" | |||
|  |             : "未知", | |||
|  |         }); | |||
|  | 
 | |||
|  |         // 显示结果 | |||
|  |         const resultDiv = document.getElementById("result"); | |||
|  |         resultDiv.style.display = "block"; | |||
|  | 
 | |||
|  |         if (isCorrect) { | |||
|  |           score += 10; | |||
|  |           resultDiv.textContent = "正确!"; | |||
|  |           resultDiv.className = "result correct"; | |||
|  | 
 | |||
|  |           // 记录正确答案 | |||
|  |           if (window.gameTracker) { | |||
|  |             window.gameTracker.recordCorrect(10, { | |||
|  |               question: q.question, | |||
|  |               selected: selected, | |||
|  |               correct: q.correct, | |||
|  |               questionNumber: currentQuestion + 1, | |||
|  |               totalQuestions: questions.length, | |||
|  |               options: q.options, | |||
|  |             }); | |||
|  |           } | |||
|  |         } else { | |||
|  |           resultDiv.textContent = `错误!正确答案是 ${q.correct}`; | |||
|  |           resultDiv.className = "result incorrect"; | |||
|  | 
 | |||
|  |           // 记录错误答案 | |||
|  |           if (window.gameTracker) { | |||
|  |             window.gameTracker.recordIncorrect(0, { | |||
|  |               question: q.question, | |||
|  |               selected: selected, | |||
|  |               correct: q.correct, | |||
|  |               questionNumber: currentQuestion + 1, | |||
|  |               totalQuestions: questions.length, | |||
|  |               options: q.options, | |||
|  |             }); | |||
|  |           } | |||
|  |         } | |||
|  | 
 | |||
|  |         // 更新得分显示 | |||
|  |         document.getElementById("score").textContent = `得分: ${score}`; | |||
|  | 
 | |||
|  |         // 禁用选项按钮 | |||
|  |         const options = document.querySelectorAll(".option-btn"); | |||
|  |         options.forEach((btn) => { | |||
|  |           btn.disabled = true; | |||
|  |           if (parseInt(btn.textContent) === q.correct) { | |||
|  |             btn.classList.add("correct"); | |||
|  |           } else if (parseInt(btn.textContent) === selected && !isCorrect) { | |||
|  |             btn.classList.add("incorrect"); | |||
|  |           } | |||
|  |         }); | |||
|  | 
 | |||
|  |         // 延迟显示下一题 | |||
|  |         setTimeout(() => { | |||
|  |           currentQuestion++; | |||
|  |           showQuestion(); | |||
|  |         }, 2000); | |||
|  |       } | |||
|  | 
 | |||
|  |       // 结束游戏 | |||
|  |       function endGame() { | |||
|  |         gameStarted = false; | |||
|  | 
 | |||
|  |         // 虚拟老师游戏结束反馈 | |||
|  |         if (window.virtualTeacher) { | |||
|  |           // 立即停止当前语音 | |||
|  |           window.virtualTeacher.stopAllSpeech(); | |||
|  | 
 | |||
|  |           // 延迟一点再播放游戏结束语音 | |||
|  |           setTimeout(() => { | |||
|  |             window.virtualTeacher.gameEnd(); | |||
|  |           }, 500); | |||
|  |         } | |||
|  | 
 | |||
|  |         // 记录游戏结果 | |||
|  |         if (window.gameTracker) { | |||
|  |           const finalScore = score; | |||
|  |           const gameData = { | |||
|  |             totalQuestions: questions.length, | |||
|  |             correctAnswers: Math.floor(score / 10), | |||
|  |             finalScore: finalScore, | |||
|  |           }; | |||
|  | 
 | |||
|  |           if (finalScore >= 30) { | |||
|  |             window.gameTracker.recordWin(finalScore, gameData); | |||
|  |           } else { | |||
|  |             window.gameTracker.recordLose(finalScore, gameData); | |||
|  |           } | |||
|  |         } | |||
|  | 
 | |||
|  |         // 显示最终结果 | |||
|  |         document.getElementById("question").textContent = "游戏结束!"; | |||
|  |         document.getElementById("options").innerHTML = ""; | |||
|  |         document.getElementById("result").style.display = "block"; | |||
|  |         document.getElementById("result").textContent = `最终得分: ${score}分`; | |||
|  |         document.getElementById("result").className = "result correct"; | |||
|  |         document.getElementById("restart-btn").style.display = "inline-block"; | |||
|  |       } | |||
|  | 
 | |||
|  |       // 重新开始游戏 | |||
|  |       function restartGame() { | |||
|  |         initGame(); | |||
|  |       } | |||
|  | 
 | |||
|  |       // 页面加载完成后初始化 | |||
|  |       document.addEventListener("DOMContentLoaded", function () { | |||
|  |         // 初始化虚拟老师 | |||
|  |         if (window.virtualTeacher) { | |||
|  |           window.virtualTeacher | |||
|  |             .init() | |||
|  |             .then(() => { | |||
|  |               console.log("🦉 虚拟老师初始化成功"); | |||
|  | 
 | |||
|  |               // 延迟播放游戏介绍,避免与访问跟踪语音冲突 | |||
|  |               setTimeout(() => { | |||
|  |                 const gameIntro = | |||
|  |                   "欢迎来到示例游戏!这是一个简单的选择题游戏,我会为你朗读题目和选项。准备好了吗?"; | |||
|  |                 window.virtualTeacher.readGameIntroduction(gameIntro); | |||
|  |               }, 1000); | |||
|  |             }) | |||
|  |             .catch((error) => { | |||
|  |               console.error("❌ 虚拟老师初始化失败:", error); | |||
|  |             }); | |||
|  |         } | |||
|  | 
 | |||
|  |         // 初始化后端集成 | |||
|  |         Promise.all([window.userManager.init(), window.dataManager.init()]) | |||
|  |           .then(() => { | |||
|  |             console.log("后端集成初始化完成"); | |||
|  |             // 自动开始游戏 | |||
|  |             initGame(); | |||
|  |           }) | |||
|  |           .catch((error) => { | |||
|  |             console.error("后端集成初始化失败:", error); | |||
|  |             // 即使后端初始化失败,也可以开始游戏 | |||
|  |             initGame(); | |||
|  |           }); | |||
|  |       }); | |||
|  |     </script> | |||
|  |   </body> | |||
|  | </html> |