710 lines
21 KiB
HTML
710 lines
21 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>
|
||
|
||
<!-- 引入后端集成脚本 -->
|
||
<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/virtualTeacher.js"></script>
|
||
<script src="../../js/gameVirtualTeacher.js"></script>
|
||
<!-- 引入体感交互脚本 -->
|
||
<script src="../../js/directBodySensation.js"></script>
|
||
|
||
<style>
|
||
:root {
|
||
--primary: #ff9aa2;
|
||
--secondary: #ffb7b2;
|
||
--accent: #ffdac1;
|
||
--correct: #b5ead7;
|
||
--wrong: #ff9aa2;
|
||
--button: #70a1ff;
|
||
--text: #5e5346;
|
||
}
|
||
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
font-family: "Comic Sans MS", "Marker Felt", "微软雅黑", sans-serif;
|
||
background-color: #f9f7f0;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
min-height: 100vh;
|
||
background-image: radial-gradient(circle, #f5f5f5 10%, transparent 10%);
|
||
background-size: 30px 30px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.game-container {
|
||
width: 80%;
|
||
background-color: white;
|
||
border-radius: 20px;
|
||
padding: 30px;
|
||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||
text-align: center;
|
||
position: relative;
|
||
overflow: hidden;
|
||
border: 5px solid #ffdac1;
|
||
}
|
||
|
||
h1 {
|
||
color: #ff6b6b;
|
||
margin-bottom: 30px;
|
||
font-size: 5rem;
|
||
text-shadow: 3px 3px 0 #ffdac1;
|
||
letter-spacing: 2px;
|
||
}
|
||
|
||
.game-area {
|
||
display: flex;
|
||
justify-content: space-around;
|
||
margin: 30px 0;
|
||
}
|
||
|
||
.math-card {
|
||
width: 45%;
|
||
padding: 20px;
|
||
background-color: #f0f8ff;
|
||
border-radius: 15px;
|
||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
|
||
position: relative;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.math-card:hover,
|
||
.math-card.active {
|
||
transform: translateY(-5px);
|
||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15);
|
||
}
|
||
|
||
.math-card::before {
|
||
content: "";
|
||
position: absolute;
|
||
top: -10px;
|
||
left: -10px;
|
||
right: -10px;
|
||
bottom: -10px;
|
||
border: 3px dashed #ffb7b2;
|
||
border-radius: 20px;
|
||
z-index: -1;
|
||
opacity: 0.5;
|
||
}
|
||
|
||
.equation {
|
||
font-size: 5rem;
|
||
margin: 20px 0;
|
||
color: #5e5346;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.select-btn {
|
||
width: 80px;
|
||
height: 80px;
|
||
border-radius: 50%;
|
||
background-color: var(--button);
|
||
color: white;
|
||
border: none;
|
||
font-size: 2rem;
|
||
font-weight: bold;
|
||
cursor: pointer;
|
||
box-shadow: 0 5px 0 #4a6baf;
|
||
transition: all 0.2s ease;
|
||
margin-top: 10px;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.select-btn:hover {
|
||
background-color: #5d8eff;
|
||
transform: scale(1.05);
|
||
}
|
||
|
||
.select-btn:active {
|
||
transform: translateY(5px);
|
||
box-shadow: 0 2px 0 #4a6baf;
|
||
}
|
||
|
||
.message {
|
||
position: fixed;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%) scale(0);
|
||
background-color: white;
|
||
padding: 30px;
|
||
border-radius: 15px;
|
||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||
z-index: 100;
|
||
text-align: center;
|
||
max-width: 80%;
|
||
transition: all 0.3s ease;
|
||
border: 5px solid;
|
||
}
|
||
|
||
.message.show {
|
||
transform: translate(-50%, -50%) scale(1);
|
||
}
|
||
|
||
.message.correct {
|
||
border-color: #b5ead7;
|
||
background-color: #e8f8f3;
|
||
}
|
||
|
||
.message.wrong {
|
||
border-color: #ff9aa2;
|
||
background-color: #ffeef0;
|
||
}
|
||
|
||
.message h2 {
|
||
font-size: 3rem;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.message p {
|
||
font-size: 2rem;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.message-btn {
|
||
padding: 10px 20px;
|
||
border-radius: 50px;
|
||
border: none;
|
||
background-color: var(--button);
|
||
color: white;
|
||
font-size: 2rem;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.message-btn:hover {
|
||
background-color: #5d8eff;
|
||
transform: scale(1.05);
|
||
}
|
||
|
||
/* 体感交互视频样式 */
|
||
.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;
|
||
}
|
||
|
||
.confetti {
|
||
position: absolute;
|
||
width: 15px;
|
||
height: 15px;
|
||
background-color: var(--primary);
|
||
opacity: 0;
|
||
z-index: 90;
|
||
}
|
||
|
||
@keyframes confetti-fall {
|
||
0% {
|
||
transform: translateY(-100vh) rotate(0deg);
|
||
opacity: 1;
|
||
}
|
||
|
||
100% {
|
||
transform: translateY(100vh) rotate(360deg);
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
@keyframes jump {
|
||
0%,
|
||
100% {
|
||
transform: translateY(0);
|
||
}
|
||
|
||
50% {
|
||
transform: translateY(-30px);
|
||
}
|
||
}
|
||
|
||
@media (max-width: 600px) {
|
||
.game-area {
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
|
||
.math-card {
|
||
width: 90%;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
h1 {
|
||
font-size: 5rem;
|
||
}
|
||
|
||
.equation {
|
||
font-size: 5rem;
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
|
||
<body>
|
||
<div class="game-container">
|
||
<h1>比大小(选择大的)</h1>
|
||
|
||
<div class="game-area">
|
||
<div class="math-card" id="leftCard">
|
||
<div class="equation" id="leftEquation">1+1</div>
|
||
<button class="select-btn" id="leftBtn">选择</button>
|
||
</div>
|
||
|
||
<div class="math-card" id="rightCard">
|
||
<div class="equation" id="rightEquation">2+2</div>
|
||
<button class="select-btn" id="rightBtn">选择</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="message" id="message">
|
||
<h2 id="messageTitle">标题</h2>
|
||
<p id="messageText">内容</p>
|
||
<button class="message-btn" id="messageBtn">继续</button>
|
||
</div>
|
||
|
||
<script>
|
||
// DOM元素
|
||
const leftEquation = document.getElementById("leftEquation");
|
||
const rightEquation = document.getElementById("rightEquation");
|
||
const leftBtn = document.getElementById("leftBtn");
|
||
const rightBtn = document.getElementById("rightBtn");
|
||
const message = document.getElementById("message");
|
||
const messageTitle = document.getElementById("messageTitle");
|
||
const messageText = document.getElementById("messageText");
|
||
const messageBtn = document.getElementById("messageBtn");
|
||
|
||
// 体感交互元素
|
||
const bodySensationContainer = document.getElementById(
|
||
"bodySensationContainer"
|
||
);
|
||
const bodyVideoFeed = document.getElementById("bodyVideoFeed");
|
||
const bodyConnectionStatus = document.getElementById(
|
||
"bodyConnectionStatus"
|
||
);
|
||
const bodyHandStatus = document.getElementById("bodyHandStatus");
|
||
const bodyInstruction = document.getElementById("bodyInstruction");
|
||
|
||
// 游戏状态
|
||
let leftValue = 0;
|
||
let rightValue = 0;
|
||
let correctAnswer = "";
|
||
let score = 0;
|
||
let correctCount = 0;
|
||
let totalQuestions = 0;
|
||
let gameStarted = false;
|
||
let gameId = 2; // 比大小游戏ID
|
||
|
||
// 体感交互控制函数
|
||
function showBodyInteraction() {
|
||
if (bodySensationContainer) {
|
||
bodySensationContainer.style.display = "block";
|
||
}
|
||
// 启动体感交互系统
|
||
if (typeof showBody === "function") {
|
||
showBody();
|
||
}
|
||
}
|
||
|
||
function hideBodyInteraction() {
|
||
if (bodySensationContainer) {
|
||
bodySensationContainer.style.display = "none";
|
||
}
|
||
}
|
||
|
||
function updateBodyInstruction(text) {
|
||
if (bodyInstruction) {
|
||
bodyInstruction.textContent = text;
|
||
}
|
||
}
|
||
|
||
// 生成5以内的加减法
|
||
function generateEquation() {
|
||
const operations = ["+", "-"];
|
||
const op = operations[Math.floor(Math.random() * operations.length)];
|
||
|
||
let a, b, result;
|
||
|
||
do {
|
||
a = Math.floor(Math.random() * 5) + 1; // 1-5
|
||
b = Math.floor(Math.random() * 5) + 1; // 1-5
|
||
|
||
if (op === "+") {
|
||
result = a + b;
|
||
} else {
|
||
result = a - b;
|
||
// 确保结果不小于0
|
||
if (result < 0) {
|
||
[a, b] = [b, a]; // 交换a和b
|
||
result = a - b;
|
||
}
|
||
}
|
||
} while (result > 5); // 确保结果不超过5
|
||
|
||
return {
|
||
equation: `${a}${op}${b}`,
|
||
value: result,
|
||
};
|
||
}
|
||
|
||
// 生成新题目
|
||
function generateNewQuestion(type) {
|
||
// 清除默认样式
|
||
document.querySelectorAll(".math-card")[0].className = "math-card";
|
||
document.querySelectorAll(".math-card")[1].className = "math-card";
|
||
|
||
if (type == "init") {
|
||
// 初始化游戏状态
|
||
score = 0;
|
||
correctCount = 0;
|
||
totalQuestions = 0;
|
||
gameStarted = true;
|
||
|
||
// 虚拟老师游戏开始
|
||
if (window.gameVirtualTeacher) {
|
||
window.gameVirtualTeacher.gameStart("比大小游戏");
|
||
}
|
||
|
||
// 开始游戏跟踪
|
||
if (window.gameTracker) {
|
||
window.gameTracker.startGame(gameId, {
|
||
gameType: "compare_size",
|
||
difficulty: "easy",
|
||
});
|
||
}
|
||
|
||
// 打开窗口
|
||
iframePostMessage();
|
||
}
|
||
|
||
const left = generateEquation();
|
||
const right = generateEquation();
|
||
|
||
leftEquation.textContent = left.equation;
|
||
rightEquation.textContent = right.equation;
|
||
|
||
leftValue = left.value;
|
||
rightValue = right.value;
|
||
|
||
// 显示体感交互视频
|
||
showBodyInteraction();
|
||
|
||
// 更新体感交互提示文本
|
||
updateBodyInstruction(
|
||
`请举起左手选择${left.equation}或举起右手选择${right.equation}`
|
||
);
|
||
|
||
correctAnswer =
|
||
leftValue > rightValue
|
||
? "left"
|
||
: rightValue > leftValue
|
||
? "right"
|
||
: "equal";
|
||
}
|
||
|
||
// 显示消息
|
||
function showMessage(isCorrect, text) {
|
||
message.className = "message " + (isCorrect ? "correct" : "wrong");
|
||
messageTitle.textContent = isCorrect ? "太棒了!" : "再试一次";
|
||
messageText.textContent = text;
|
||
message.classList.add("show");
|
||
|
||
if (isCorrect) {
|
||
createConfetti();
|
||
}
|
||
}
|
||
|
||
// 创建彩色纸屑效果
|
||
function createConfetti() {
|
||
const colors = [
|
||
"#FF9AA2",
|
||
"#FFB7B2",
|
||
"#FFDAC1",
|
||
"#E2F0CB",
|
||
"#B5EAD7",
|
||
"#C7CEEA",
|
||
];
|
||
|
||
for (let i = 0; i < 50; i++) {
|
||
const confetti = document.createElement("div");
|
||
confetti.className = "confetti";
|
||
confetti.style.left = `${Math.random() * 100}%`;
|
||
confetti.style.backgroundColor =
|
||
colors[Math.floor(Math.random() * colors.length)];
|
||
confetti.style.width = `${Math.random() * 10 + 5}px`;
|
||
confetti.style.height = `${Math.random() * 10 + 5}px`;
|
||
confetti.style.animation = `confetti-fall ${
|
||
Math.random() * 2 + 2
|
||
}s linear forwards`;
|
||
confetti.style.animationDelay = `${Math.random() * 0.5}s`;
|
||
|
||
document.body.appendChild(confetti);
|
||
|
||
setTimeout(() => {
|
||
confetti.remove();
|
||
}, 3000);
|
||
}
|
||
}
|
||
|
||
// 检查答案
|
||
function checkAnswer(selectedSide) {
|
||
console.log(
|
||
"checkAnswer message",
|
||
document.getElementById("message"),
|
||
document.getElementById("message").classList.contains("show")
|
||
);
|
||
|
||
if (document.getElementById("message").classList.contains("show")) {
|
||
return;
|
||
}
|
||
|
||
// 隐藏体感交互按钮
|
||
hideBodyInteraction();
|
||
|
||
let isCorrect = false;
|
||
let message = "";
|
||
|
||
document.querySelectorAll(".math-card")[
|
||
selectedSide == "left" ? 0 : 1
|
||
].className = "math-card active";
|
||
|
||
if (correctAnswer === "equal") {
|
||
isCorrect = true;
|
||
message = "两边一样大!";
|
||
} else if (selectedSide === correctAnswer) {
|
||
isCorrect = true;
|
||
message = `对了!${leftValue} ${
|
||
selectedSide === "left" ? ">" : "<"
|
||
} ${rightValue}`;
|
||
} else {
|
||
isCorrect = false;
|
||
message = `不对哦!${leftValue} ${
|
||
correctAnswer === "left" ? ">" : "<"
|
||
} ${rightValue}`;
|
||
}
|
||
|
||
// 虚拟老师反馈
|
||
if (window.gameVirtualTeacher) {
|
||
// 立即停止当前语音
|
||
window.gameVirtualTeacher.stopAllSpeech();
|
||
|
||
// 延迟一点再播放反馈语音
|
||
setTimeout(() => {
|
||
if (isCorrect) {
|
||
window.gameVirtualTeacher.recordCorrect();
|
||
} else {
|
||
window.gameVirtualTeacher.recordIncorrect();
|
||
}
|
||
}, 300);
|
||
}
|
||
|
||
// 详细数据打印
|
||
console.log("🎯 比大小游戏 - 用户点击记录");
|
||
console.log("📊 问题信息:", {
|
||
问题内容: `${leftValue} vs ${rightValue}`,
|
||
左侧值: leftValue,
|
||
右侧值: rightValue,
|
||
正确答案: correctAnswer,
|
||
用户选择: selectedSide,
|
||
是否正确: isCorrect ? "✅ 正确" : "❌ 错误",
|
||
});
|
||
console.log("📈 游戏进度:", {
|
||
当前得分: score,
|
||
正确次数: correctCount,
|
||
总问题数: totalQuestions + 1,
|
||
正确率:
|
||
totalQuestions > 0
|
||
? `${((correctCount / (totalQuestions + 1)) * 100).toFixed(1)}%`
|
||
: "0%",
|
||
});
|
||
console.log("⏰ 时间信息:", {
|
||
回答时间: new Date().toLocaleString("zh-CN"),
|
||
游戏时长: window.gameTracker
|
||
? window.gameTracker.getCurrentPlayTime() + "秒"
|
||
: "未知",
|
||
});
|
||
|
||
// 记录游戏结果
|
||
if (gameStarted && window.gameTracker) {
|
||
totalQuestions++;
|
||
if (isCorrect) {
|
||
correctCount++;
|
||
score += 10;
|
||
window.gameTracker.recordCorrect(10, {
|
||
question: `${leftValue} vs ${rightValue}`,
|
||
selected: selectedSide,
|
||
correct: correctAnswer,
|
||
leftValue: leftValue,
|
||
rightValue: rightValue,
|
||
questionNumber: totalQuestions,
|
||
});
|
||
} else {
|
||
window.gameTracker.recordIncorrect(0, {
|
||
question: `${leftValue} vs ${rightValue}`,
|
||
selected: selectedSide,
|
||
correct: correctAnswer,
|
||
leftValue: leftValue,
|
||
rightValue: rightValue,
|
||
questionNumber: totalQuestions,
|
||
});
|
||
}
|
||
}
|
||
|
||
showMessage(isCorrect, message);
|
||
}
|
||
|
||
// 事件监听
|
||
leftBtn.addEventListener("click", (event) => checkAnswer("left"));
|
||
rightBtn.addEventListener("click", (event) => checkAnswer("right"));
|
||
|
||
messageBtn.addEventListener("click", () => {
|
||
message.classList.remove("show");
|
||
generateNewQuestion("continue");
|
||
});
|
||
|
||
// 页面加载完成后初始化
|
||
document.addEventListener("DOMContentLoaded", function () {
|
||
// 初始化虚拟老师
|
||
if (window.gameVirtualTeacher) {
|
||
window.gameVirtualTeacher
|
||
.init({
|
||
intro: "欢迎来到比大小游戏!我会帮你比较数字的大小,准备好了吗?",
|
||
})
|
||
.then(() => {
|
||
console.log("🦉 比大小游戏虚拟老师初始化成功");
|
||
|
||
// 延迟播放游戏介绍,避免与访问跟踪语音冲突
|
||
setTimeout(() => {
|
||
window.gameVirtualTeacher.speak(
|
||
"欢迎来到比大小游戏!我会帮你比较数字的大小,准备好了吗?"
|
||
);
|
||
}, 1000);
|
||
})
|
||
.catch((error) => {
|
||
console.error("❌ 虚拟老师初始化失败:", error);
|
||
});
|
||
}
|
||
|
||
// 初始化后端集成
|
||
Promise.all([window.userManager.init(), window.dataManager.init()])
|
||
.then(() => {
|
||
console.log("后端集成初始化完成");
|
||
// 初始化游戏
|
||
generateNewQuestion("init");
|
||
})
|
||
.catch((error) => {
|
||
console.error("后端集成初始化失败:", error);
|
||
// 即使后端初始化失败,也可以开始游戏
|
||
generateNewQuestion("init");
|
||
});
|
||
});
|
||
|
||
// 网页间相互通信
|
||
window.addEventListener("message", function (event) {
|
||
// 检查消息来源是否可信
|
||
if (event.origin !== "http://localhost") return;
|
||
|
||
const message = event.data;
|
||
console.log("iframe 接收到的数据:", message);
|
||
|
||
if (message.type == "语音识别") {
|
||
} else if (message.type == "体感识别") {
|
||
if (message.data.indexOf("左手") !== -1) {
|
||
checkAnswer("left");
|
||
} else if (message.data.indexOf("右手") !== -1) {
|
||
checkAnswer("right");
|
||
} else if (message.data.indexOf("双手") !== -1) {
|
||
messageBtn.click();
|
||
}
|
||
}
|
||
});
|
||
|
||
function iframePostMessage() {
|
||
const message = { type: "体感识别", status: "request" };
|
||
window.parent.postMessage(message, "http://localhost"); // 替换为目标域名
|
||
}
|
||
|
||
// 页面销毁时,关闭体感采集
|
||
window.onbeforeunload = function (event) {
|
||
iframePostMessage();
|
||
};
|
||
</script>
|
||
</body>
|
||
</html>
|