11.14 脚本网页 迷宫逃离
一 博主今天摸鱼,闲着没事儿写了一个迷宫逃离的游戏。
二 代码不做解释自己,可问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();
});
</script>
</body>
</html>
