一 博主今天摸鱼,闲着没事儿写了一个迷宫逃离的游戏。
二 代码不做解释自己,可问AI
代码开源,后续优化自己随意

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>迷宫逃脱·最终版</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
-webkit-user-select: none;
user-select: none;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #0d1b2a, #1f3a5f, #00ffcc);
color: white;
height: 100vh;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 10px;
transition: background 0.5s ease;
}
#game-container {
position: relative;
width: 100%;
height: 100%;
max-width: 400px;
max-height: 700px;
display: flex;
flex-direction: column;
}
#start-screen, #game-screen, #end-screen {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
transition: opacity 0.3s;
}
#game-screen {
display: none;
}
#end-screen {
display: none;
}
h1 {
font-size: 2.2rem;
margin-bottom: 15px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
color: #FFD700;
}
h2 {
font-size: 1.6rem;
margin-bottom: 10px;
color: #4CAF50;
}
p {
font-size: 1rem;
margin-bottom: 15px;
line-height: 1.4;
}
.difficulty-option {
width: 90%;
padding: 12px;
margin: 8px 0;
background: rgba(255, 255, 255, 0.15);
border: 2px solid;
border-radius: 12px;
font-size: 1.1rem;
color: white;
cursor: pointer;
transition: all 0.2s;
}
.difficulty-option:active {
transform: scale(0.98);
}
.difficulty-option.easy {
border-color: #4CAF50;
background: rgba(76, 175, 80, 0.2);
}
.difficulty-option.normal {
border-color: #FF9800;
background: rgba(255, 152, 0, 0.2);
}
.difficulty-option.hard {
border-color: #F44336;
background: rgba(244, 67, 54, 0.2);
}
#game-area {
position: relative;
width: 100%;
flex: 1;
background: #0d1b2a;
border-radius: 8px;
overflow: hidden;
margin-bottom: 10px;
}
#game-canvas {
display: block;
width: 100%;
height: 100%;
}
#ui {
display: flex;
justify-content: space-around;
width: 100%;
background: rgba(0, 0, 0, 0.6);
padding: 8px;
border-radius: 8px;
font-size: 1rem;
margin-bottom: 10px;
}
#controls {
display: flex;
justify-content: space-between;
width: 100%;
height: 120px;
}
.d-pad {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
gap: 5px;
width: 120px;
height: 120px;
}
.control-btn {
background: rgba(255, 255, 255, 0.2);
border: 2px solid rgba(255, 255, 255, 0.4);
border-radius: 8px;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
cursor: pointer;
transition: all 0.1s;
}
.control-btn:active {
background: rgba(255, 255, 255, 0.4);
transform: scale(0.95);
}
.control-btn.center {
grid-column: 2;
grid-row: 2;
visibility: hidden;
}
#action-buttons {
display: flex;
flex-direction: column;
justify-content: space-between;
width: 120px;
}
.action-btn {
height: 55px;
background: rgba(76, 175, 80, 0.3);
border: 2px solid #4CAF50;
border-radius: 8px;
color: white;
font-size: 1rem;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.action-btn:active {
background: rgba(76, 175, 80, 0.5);
}
button {
padding: 10px 20px;
margin: 8px;
background: #4CAF50;
border: none;
border-radius: 20px;
font-size: 1rem;
color: white;
cursor: pointer;
transition: all 0.2s;
}
button:active {
transform: scale(0.95);
}
#mute-btn {
position: absolute;
top: 10px;
right: 10px;
width: 36px;
height: 36px;
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 1rem;
}
#theme-btn {
position: absolute;
top: 10px;
left: 10px;
width: 36px;
height: 36px;
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 0.8rem;
}
.instructions {
font-size: 0.9rem;
color: #ccc;
margin-top: 10px;
}
.score-display {
position: absolute;
top: 60px;
right: 10px;
background: rgba(0, 0, 0, 0.6);
padding: 5px 10px;
border-radius: 10px;
font-size: 0.9rem;
}
</style>
</head>
<body>
<div id="game-container">
<div id="start-screen">
<h1>迷宫逃脱</h1>
<p>收集所有钥匙,避开敌人,找到出口!</p>
<p>当前积分: <span id="total-score">0</span></p>
<p>选择难度开始游戏:</p>
<p style="color:#FFD700; font-size:0.9rem;">⚠️ 必须先收集 5 把钥匙,否则出口无效!</p>
<div class="difficulty-option easy" data-difficulty="easy">简单模式 - 敌人速度慢,不会穿墙</div>
<div class="difficulty-option normal" data-difficulty="normal">普通模式 - 敌人速度中等</div>
<div class="difficulty-option hard" data-difficulty="hard">困难模式 - 敌人速度快</div>
<p class="instructions">使用方向键控制角色移动</p>
</div>
<div id="game-screen">
<div id="game-area">
<canvas id="game-canvas"></canvas>
</div>
<div id="ui">
<div>生命: <span id="hp">3</span></div>
<div>钥匙: <span id="keys">0</span>/5</div>
<div>时间: <span id="time">60</span>s</div>
</div>
<p style="color:#FFD700; font-size:0.9rem;">⚠️ 必须先收集 5 把钥匙,否则出口无效!</p>
<div id="controls">
<div class="d-pad">
<div class="control-btn"></div>
<div class="control-btn up" id="up-btn">↑</div>
<div class="control-btn"></div>
<div class="control-btn left" id="left-btn">←</div>
<div class="control-btn center"></div>
<div class="control-btn right" id="right-btn">→</div>
<div class="control-btn"></div>
<div class="control-btn down" id="down-btn">↓</div>
<div class="control-btn"></div>
</div>
<div id="action-buttons">
<div class="action-btn" id="pause-btn">暂停</div>
<div class="action-btn" id="restart-game-btn">重来</div>
</div>
</div>
</div>
<div id="end-screen">
<h2 id="result-message"></h2>
<p id="result-details"></p>
<p>本局得分: <span id="round-score">0</span></p>
<p>总积分: <span id="total-score-end">0</span></p>
<button id="restart-btn">再玩一次</button>
<button id="menu-btn">返回主菜单</button>
</div>
<div id="mute-btn">🔊</div>
<div id="theme-btn">🎨</div>
<div class="score-display">积分: <span id="score-display">0</span></div>
</div>
<script>
// 游戏配置
const CONFIG = {
canvasWidth: 360,
canvasHeight: 480,
keysRequired: 5,
initialLives: 3,
gameTime: 60
};
// 难度设置
const DIFFICULTY = {
easy: { enemySpeed: 0.8, playerSpeed: 3.5, scoreMultiplier: 1 },
normal: { enemySpeed: 1.5, playerSpeed: 3.5, scoreMultiplier: 2 },
hard: { enemySpeed: 2.5, playerSpeed: 3.5, scoreMultiplier: 3 }
};
// 背景主题颜色数组
const THEMES = [
'linear-gradient(135deg, #0f2027, #203a43, #2c5364)',
'linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d)',
'linear-gradient(135deg, #3a1c71, #d76d77, #ffaf7b)',
'linear-gradient(135deg, #2193b0, #6dd5ed)',
'linear-gradient(135deg, #834d9b, #d04ed6)',
'linear-gradient(135deg, #0f0c29, #302b63, #24243e)',
'linear-gradient(135deg, #c31432, #240b36)',
'linear-gradient(135deg, #7b4397, #dc2430)',
'linear-gradient(135deg, #00b4db, #0083b0)',
'linear-gradient(135deg, #fd746c, #ff9068)',
'linear-gradient(135deg, #556270, #ff6b6b)',
'linear-gradient(135deg, #4ca1af, #2c3e50)',
'linear-gradient(135deg, #ff9a9e, #fecfef)',
'linear-gradient(135deg, #a8ff78, #78ffd6)',
'linear-gradient(135deg, #f46b45, #eea849)'
];
// 游戏状态
let gameState = {
currentScreen: 'start',
difficulty: 'normal',
player: null,
enemies: [],
keys: [],
walls: [],
particles: [],
lives: CONFIG.initialLives,
keysCollected: 0,
timeLeft: CONFIG.gameTime,
invincible: 0,
gameOver: false,
paused: false,
maze: null,
keysPressed: {},
totalScore: 0,
currentRoundScore: 0,
currentTheme: 0
};
// DOM元素
const startScreen = document.getElementById('start-screen');
const gameScreen = document.getElementById('game-screen');
const endScreen = document.getElementById('end-screen');
const gameCanvas = document.getElementById('game-canvas');
const ctx = gameCanvas.getContext('2d');
const hpElement = document.getElementById('hp');
const keysElement = document.getElementById('keys');
const timeElement = document.getElementById('time');
const resultMessage = document.getElementById('result-message');
const resultDetails = document.getElementById('result-details');
const restartBtn = document.getElementById('restart-btn');
const menuBtn = document.getElementById('menu-btn');
const muteBtn = document.getElementById('mute-btn');
const themeBtn = document.getElementById('theme-btn');
const pauseBtn = document.getElementById('pause-btn');
const restartGameBtn = document.getElementById('restart-game-btn');
const scoreDisplay = document.getElementById('score-display');
const totalScoreElement = document.getElementById('total-score');
const totalScoreEndElement = document.getElementById('total-score-end');
const roundScoreElement = document.getElementById('round-score');
// 音频上下文
let audioContext;
let muted = false;
// 初始化游戏
function initGame() {
// 设置画布尺寸
gameCanvas.width = CONFIG.canvasWidth;
gameCanvas.height = CONFIG.canvasHeight;
// 生成迷宫 - 确保出口连通
const mazeWidth = 9;
const mazeHeight = 12;
const cellSize = Math.min(CONFIG.canvasWidth / mazeWidth, CONFIG.canvasHeight / mazeHeight);
gameState.maze = generateMazeWithConnectedExit(mazeWidth, mazeHeight);
gameState.walls = createWallsFromMaze(gameState.maze, cellSize);
// 创建玩家
gameState.player = {
x: cellSize * 1.5,
y: cellSize + cellSize/2,
radius: 8,
dx: 0,
dy: 0
};
// 创建敌人
gameState.enemies = [];
const enemyCount = gameState.difficulty === 'easy' ? 2 :
gameState.difficulty === 'normal' ? 3 : 4;
for (let i = 0; i < enemyCount; i++) {
let enemyX, enemyY;
let attempts = 0;
do {
enemyX = Math.floor(Math.random() * (mazeWidth-2)) + 1;
enemyY = Math.floor(Math.random() * (mazeHeight-2)) + 1;
attempts++;
if (attempts > 100) break;
} while (gameState.maze[enemyY][enemyX] !== 0 ||
(enemyX <= 2 && enemyY <= 2) ||
(enemyX === mazeWidth-2 && enemyY === mazeHeight-2));
if (attempts <= 100) {
gameState.enemies.push({
x: enemyX * cellSize + cellSize/2,
y: enemyY * cellSize + cellSize/2,
radius: 10
});
}
}
// 创建钥匙
gameState.keys = [];
for (let i = 0; i < CONFIG.keysRequired; i++) {
let keyX, keyY;
do {
keyX = Math.floor(Math.random() * (mazeWidth-2)) + 1;
keyY = Math.floor(Math.random() * (mazeHeight-2)) + 1;
} while (gameState.maze[keyY][keyX] !== 0 ||
(keyX <= 2 && keyY <= 2) ||
(keyX === mazeWidth-2 && keyY === mazeHeight-2));
gameState.keys.push({
x: keyX * cellSize + cellSize/2,
y: keyY * cellSize + cellSize/2,
radius: 6,
collected: false
});
}
// 重置游戏状态
gameState.lives = CONFIG.initialLives;
gameState.keysCollected = 0;
gameState.timeLeft = CONFIG.gameTime;
gameState.invincible = 0;
gameState.gameOver = false;
gameState.paused = false;
gameState.particles = [];
gameState.keysPressed = {};
gameState.currentRoundScore = 0;
// 更新UI
hpElement.textContent = gameState.lives;
keysElement.textContent = gameState.keysCollected;
timeElement.textContent = gameState.timeLeft;
pauseBtn.textContent = '暂停';
scoreDisplay.textContent = gameState.totalScore;
totalScoreElement.textContent = gameState.totalScore;
}
// 生成迷宫,确保出口连通
function generateMazeWithConnectedExit(width, height) {
// 创建网格,初始全部为墙
const grid = Array(height).fill().map(() => Array(width).fill(1));
// 使用深度优先搜索生成迷宫
function carve(x, y) {
grid[y][x] = 0;
const directions = [
0, -2\], \[2, 0\], \[0, 2\], \[-2, 0
];
for (let i = directions.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
directions\[i\], directions\[j\]\] = \[directions\[j\], directions\[i\]\]; } for (const \[dx, dy\] of directions) { const nx = x + dx, ny = y + dy; if (nx \> 0 \&\& nx \< width - 1 \&\& ny \> 0 \&\& ny \< height - 1 \&\& grid\[ny\]\[nx\] === 1) { grid\[y + dy/2\]\[x + dx/2\] = 0; carve(nx, ny); } } } // 从起点开始生成 carve(1, 1); // 确保出口连通 - 这是关键修复 grid\[height-2\]\[width-2\] = 0; // 确保有一条路径连接到出口 if (grid\[height-3\]\[width-2\] === 1) { grid\[height-3\]\[width-2\] = 0; } if (grid\[height-2\]\[width-3\] === 1) { grid\[height-2\]\[width-3\] = 0; } // 创建第二条路径 createSecondPath(grid, width, height); // ✅ 清空出口周围 3×3 区域,确保出口是开放的 const exitX = width - 2; const exitY = height - 2; for (let dy = -1; dy \<= 1; dy++) { for (let dx = -1; dx \<= 1; dx++) { const nx = exitX + dx; const ny = exitY + dy; if (nx \>= 0 \&\& nx \< width \&\& ny \>= 0 \&\& ny \< height) { grid\[ny\]\[nx\] = 0; } } } // ✅ 再打通一条从起点到出口的路径(避免被墙挡住) let cx = 1, cy = 1; while (cx !== exitX \|\| cy !== exitY) { if (cx \< exitX) { grid\[cy\]\[cx + 1\] = 0; cx++; } else if (cy \< exitY) { grid\[cy + 1\]\[cx\] = 0; cy++; } } return grid; } // 创建第二条路径 function createSecondPath(grid, width, height) { // 从右下角向左上角创建一条路径 let x = width-2, y = height-2; while (x \> 1 \|\| y \> 1) { if (x \> 1 \&\& (Math.random() \< 0.5 \|\| y === 1)) { grid\[y\]\[x-1\] = 0; x--; } else if (y \> 1) { grid\[y-1\]\[x\] = 0; y--; } } } // 从迷宫网格创建墙壁 function createWallsFromMaze(maze, cellSize) { const walls = \[\]; const height = maze.length; const width = maze\[0\].length; for (let y = 0; y \< height; y++) { for (let x = 0; x \< width; x++) { if (maze\[y\]\[x\] === 1) { walls.push({ x: x \* cellSize, y: y \* cellSize, width: cellSize, height: cellSize }); } } } return walls; } // 游戏主循环 function gameLoop() { if (gameState.gameOver \|\| gameState.paused) { requestAnimationFrame(gameLoop); return; } update(); render(); requestAnimationFrame(gameLoop); } // 更新游戏状态 function update() { const player = gameState.player; const difficultySettings = DIFFICULTY\[gameState.difficulty\]; // 更新玩家位置 player.dx = 0; player.dy = 0; if (gameState.keysPressed\['ArrowUp'\] \|\| gameState.keysPressed\['up'\]) { player.dy = -difficultySettings.playerSpeed; } if (gameState.keysPressed\['ArrowDown'\] \|\| gameState.keysPressed\['down'\]) { player.dy = difficultySettings.playerSpeed; } if (gameState.keysPressed\['ArrowLeft'\] \|\| gameState.keysPressed\['left'\]) { player.dx = -difficultySettings.playerSpeed; } if (gameState.keysPressed\['ArrowRight'\] \|\| gameState.keysPressed\['right'\]) { player.dx = difficultySettings.playerSpeed; } if (player.dx !== 0 \&\& player.dy !== 0) { player.dx \*= 0.707; player.dy \*= 0.707; } const oldX = player.x; const oldY = player.y; player.x += player.dx; player.y += player.dy; player.x = Math.max(player.radius, Math.min(CONFIG.canvasWidth - player.radius, player.x)); player.y = Math.max(player.radius, Math.min(CONFIG.canvasHeight - player.radius, player.y)); let collided = false; gameState.walls.forEach(wall =\> { if (circleRectCollision(player, wall)) { collided = true; if (player.dx \> 0 \&\& player.x - player.radius \< wall.x) { player.x = wall.x - player.radius; } else if (player.dx \< 0 \&\& player.x + player.radius \> wall.x + wall.width) { player.x = wall.x + wall.width + player.radius; } if (player.dy \> 0 \&\& player.y - player.radius \< wall.y) { player.y = wall.y - player.radius; } else if (player.dy \< 0 \&\& player.y + player.radius \> wall.y + wall.height) { player.y = wall.y + wall.height + player.radius; } } }); if (!collided) { for (const wall of gameState.walls) { if (circleRectCollision(player, wall)) { player.x = oldX; player.y = oldY; break; } } } // 更新敌人位置 gameState.enemies.forEach(enemy =\> { const angle = Math.atan2(player.y - enemy.y, player.x - enemy.x); const nextX = enemy.x + Math.cos(angle) \* difficultySettings.enemySpeed; const nextY = enemy.y + Math.sin(angle) \* difficultySettings.enemySpeed; if (gameState.difficulty === 'easy') { let canMove = true; for (const wall of gameState.walls) { if (circleRectCollision({x: nextX, y: nextY, radius: enemy.radius}, wall)) { canMove = false; break; } } if (canMove) { enemy.x = nextX; enemy.y = nextY; } } else { enemy.x = nextX; enemy.y = nextY; gameState.walls.forEach(wall =\> { if (circleRectCollision(enemy, wall)) { if (enemy.x - enemy.radius \< wall.x) { enemy.x = wall.x + enemy.radius; } else if (enemy.x + enemy.radius \> wall.x + wall.width) { enemy.x = wall.x + wall.width - enemy.radius; } if (enemy.y - enemy.radius \< wall.y) { enemy.y = wall.y + enemy.radius; } else if (enemy.y + enemy.radius \> wall.y + wall.height) { enemy.y = wall.y + wall.height - enemy.radius; } } }); } if (gameState.invincible \<= 0 \&\& circleCircleCollision(player, enemy)) { playerHit(); } }); // 钥匙收集 gameState.keys.forEach(key =\> { if (!key.collected \&\& circleCircleCollision(player, key)) { key.collected = true; gameState.keysCollected++; keysElement.textContent = gameState.keysCollected; playSound('collect'); for (let i = 0; i \< 15; i++) { gameState.particles.push({ x: key.x, y: key.y, vx: (Math.random() - 0.5) \* 4, vy: (Math.random() - 0.5) \* 4, life: 30, color: '#FFD700' }); } } }); // ✅ 新出口检测:玩家中心碰线即通关 if (gameState.keysCollected \>= CONFIG.keysRequired) { const exitLineX = CONFIG.canvasWidth - 40; // 出口左边线 if (gameState.player.x \>= exitLineX) { endGame(true); } } if (gameState.invincible \> 0) { gameState.invincible--; } gameState.particles = gameState.particles.filter(p =\> { p.x += p.vx; p.y += p.vy; p.life--; return p.life \> 0; }); } // 渲染游戏 function render() { ctx.clearRect(0, 0, CONFIG.canvasWidth, CONFIG.canvasHeight); ctx.fillStyle = '#0d1b2a'; ctx.fillRect(0, 0, CONFIG.canvasWidth, CONFIG.canvasHeight); ctx.fillStyle = '#415a77'; gameState.walls.forEach(wall =\> { ctx.fillRect(wall.x, wall.y, wall.width, wall.height); }); ctx.fillStyle = gameState.keysCollected \>= CONFIG.keysRequired ? '#4CAF50' : '#777'; ctx.fillRect(CONFIG.canvasWidth - 40, CONFIG.canvasHeight - 40, 40, 40); ctx.fillStyle = 'white'; ctx.font = '12px Arial'; ctx.textAlign = 'center'; ctx.fillText('出口', CONFIG.canvasWidth - 20, CONFIG.canvasHeight - 20); ctx.fillStyle = '#FFD700'; gameState.keys.forEach(key =\> { if (!key.collected) { ctx.beginPath(); ctx.arc(key.x, key.y, key.radius, 0, Math.PI \* 2); ctx.fill(); } }); ctx.fillStyle = '#f44336'; gameState.enemies.forEach(enemy =\> { ctx.beginPath(); ctx.arc(enemy.x, enemy.y, enemy.radius, 0, Math.PI \* 2); ctx.fill(); ctx.fillStyle = 'white'; ctx.beginPath(); ctx.arc(enemy.x - 3, enemy.y - 2, 2, 0, Math.PI \* 2); ctx.arc(enemy.x + 3, enemy.y - 2, 2, 0, Math.PI \* 2); ctx.fill(); ctx.fillStyle = '#f44336'; }); ctx.fillStyle = gameState.invincible % 6 \< 3 ? '#4CAF50' : '#81C784'; ctx.beginPath(); ctx.arc(gameState.player.x, gameState.player.y, gameState.player.radius, 0, Math.PI \* 2); ctx.fill(); ctx.fillStyle = 'white'; ctx.beginPath(); ctx.arc(gameState.player.x - 2, gameState.player.y - 1, 1.5, 0, Math.PI \* 2); ctx.arc(gameState.player.x + 2, gameState.player.y - 1, 1.5, 0, Math.PI \* 2); ctx.fill(); gameState.particles.forEach(p =\> { ctx.fillStyle = p.color; ctx.globalAlpha = p.life / 30; ctx.fillRect(p.x - 2, p.y - 2, 4, 4); }); ctx.globalAlpha = 1; if (gameState.paused) { ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'; ctx.fillRect(0, 0, CONFIG.canvasWidth, CONFIG.canvasHeight); ctx.fillStyle = 'white'; ctx.font = 'bold 24px Arial'; ctx.textAlign = 'center'; ctx.fillText('游戏暂停', CONFIG.canvasWidth / 2, CONFIG.canvasHeight / 2); } } // 玩家被击中 function playerHit() { gameState.lives--; gameState.invincible = 60; hpElement.textContent = gameState.lives; playSound('hit'); for (let i = 0; i \< 20; i++) { gameState.particles.push({ x: gameState.player.x, y: gameState.player.y, vx: (Math.random() - 0.5) \* 5, vy: (Math.random() - 0.5) \* 5, life: 30, color: '#f44336' }); } const angle = Math.atan2(gameState.player.y - CONFIG.canvasHeight/2, gameState.player.x - CONFIG.canvasWidth/2); gameState.player.x += Math.cos(angle) \* 30; gameState.player.y += Math.sin(angle) \* 30; if (gameState.lives \<= 0) { endGame(false); } } // 结束游戏 function endGame(win) { gameState.gameOver = true; // 计算得分 const difficultySettings = DIFFICULTY\[gameState.difficulty\]; let roundScore = 0; if (win) { // 基础分 + 时间分 + 生命分 + 难度乘数 roundScore = 1000 + (gameState.timeLeft \* 10) + (gameState.lives \* 200); roundScore \*= difficultySettings.scoreMultiplier; resultMessage.textContent = '逃脱成功!'; resultMessage.style.color = '#4CAF50'; resultDetails.textContent = \`你成功收集了所有钥匙并逃出了迷宫!\`; playSound('win'); } else { // 失败也有参与分 roundScore = gameState.keysCollected \* 100 \* difficultySettings.scoreMultiplier; resultMessage.textContent = '游戏结束'; resultMessage.style.color = '#f44336'; resultDetails.textContent = \`时间耗尽或生命值归零!\`; playSound('lose'); } gameState.currentRoundScore = roundScore; gameState.totalScore += roundScore; roundScoreElement.textContent = roundScore; totalScoreEndElement.textContent = gameState.totalScore; gameScreen.style.display = 'none'; endScreen.style.display = 'flex'; } // 播放音效 function playSound(type) { if (muted \|\| !audioContext) return; try { const oscillator = audioContext.createOscillator(); const gainNode = audioContext.createGain(); oscillator.connect(gainNode); gainNode.connect(audioContext.destination); let frequency = 440; let duration = 0.1; switch(type) { case 'collect': frequency = 1000; duration = 0.2; break; case 'hit': frequency = 300; duration = 0.3; oscillator.type = 'sawtooth'; break; case 'win': frequency = 523; const frequencies = \[523, 659, 784, 1047\]; frequencies.forEach((freq, i) =\> { setTimeout(() =\> { const osc = audioContext.createOscillator(); const gn = audioContext.createGain(); osc.connect(gn); gn.connect(audioContext.destination); osc.frequency.value = freq; osc.type = 'sine'; gn.gain.setValueAtTime(0.1, audioContext.currentTime); gn.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.15); osc.start(); osc.stop(audioContext.currentTime + 0.15); }, i \* 200); }); return; case 'lose': frequency = 220; duration = 0.5; oscillator.type = 'sawtooth'; break; default: frequency = 800; duration = 0.1; } oscillator.frequency.value = frequency; oscillator.type = oscillator.type \|\| 'sine'; gainNode.gain.setValueAtTime(0.1, audioContext.currentTime); gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + duration); oscillator.start(); oscillator.stop(audioContext.currentTime + duration); } catch (e) { console.log("Audio error:", e); } } // 切换主题 function switchTheme() { gameState.currentTheme = (gameState.currentTheme + 1) % THEMES.length; document.body.style.background = THEMES\[gameState.currentTheme\]; playSound('click'); } // 碰撞检测函数 function circleCircleCollision(c1, c2) { const dx = c1.x - c2.x; const dy = c1.y - c2.y; const distance = Math.sqrt(dx \* dx + dy \* dy); return distance \< c1.radius + c2.radius; } function circleRectCollision(circle, rect) { const closestX = Math.max(rect.x, Math.min(circle.x, rect.x + rect.width)); const closestY = Math.max(rect.y, Math.min(circle.y, rect.y + rect.height)); const distanceX = circle.x - closestX; const distanceY = circle.y - closestY; return (distanceX \* distanceX + distanceY \* distanceY) \< (circle.radius \* circle.radius); } // 初始化事件监听 function initEventListeners() { document.querySelectorAll('.difficulty-option').forEach(option =\> { option.addEventListener('click', function() { playSound('click'); gameState.difficulty = this.getAttribute('data-difficulty'); startScreen.style.display = 'none'; gameScreen.style.display = 'flex'; initGame(); startGame(); }); }); document.addEventListener('keydown', (e) =\> { if (\['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'\].includes(e.key)) { gameState.keysPressed\[e.key\] = true; e.preventDefault(); } }); document.addEventListener('keyup', (e) =\> { if (\['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'\].includes(e.key)) { gameState.keysPressed\[e.key\] = false; e.preventDefault(); } }); function setupButtonControl(buttonId, key) { const button = document.getElementById(buttonId); button.addEventListener('mousedown', () =\> { gameState.keysPressed\[key\] = true; }); button.addEventListener('mouseup', () =\> { gameState.keysPressed\[key\] = false; }); button.addEventListener('mouseleave', () =\> { gameState.keysPressed\[key\] = false; }); button.addEventListener('touchstart', (e) =\> { e.preventDefault(); gameState.keysPressed\[key\] = true; }); button.addEventListener('touchend', (e) =\> { e.preventDefault(); gameState.keysPressed\[key\] = false; }); } setupButtonControl('up-btn', 'up'); setupButtonControl('down-btn', 'down'); setupButtonControl('left-btn', 'left'); setupButtonControl('right-btn', 'right'); pauseBtn.addEventListener('click', () =\> { playSound('click'); gameState.paused = !gameState.paused; pauseBtn.textContent = gameState.paused ? '继续' : '暂停'; }); restartGameBtn.addEventListener('click', () =\> { playSound('click'); initGame(); }); restartBtn.addEventListener('click', () =\> { playSound('click'); endScreen.style.display = 'none'; gameScreen.style.display = 'flex'; initGame(); startGame(); }); menuBtn.addEventListener('click', () =\> { playSound('click'); endScreen.style.display = 'none'; startScreen.style.display = 'flex'; }); muteBtn.addEventListener('click', () =\> { muted = !muted; muteBtn.textContent = muted ? '🔇' : '🔊'; playSound('click'); }); themeBtn.addEventListener('click', () =\> { switchTheme(); }); document.addEventListener('touchmove', (e) =\> { if (e.target.tagName !== 'CANVAS') { e.preventDefault(); } }, { passive: false }); } // 开始游戏 function startGame() { try { audioContext = new (window.AudioContext \|\| window.webkitAudioContext)(); } catch (e) { console.log("Web Audio API not supported"); } const timer = setInterval(() =\> { if (gameState.gameOver \|\| gameState.paused) { if (gameState.gameOver) clearInterval(timer); return; } gameState.timeLeft--; timeElement.textContent = gameState.timeLeft; if (gameState.timeLeft \<= 0) { endGame(false); clearInterval(timer); } }, 1000); gameLoop(); } window.addEventListener('load', () =\> { initEventListeners(); }); \ \