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>
|