经典贪吃蛇
纯前端代码实现经典贪吃蛇游戏,含电脑端方向键控制,暂停,难度模式选择,以及特殊食物和保存历史最高分等功能。新增支持移动端控制
控制: 方向键 ↑↓←→ 或 WASD、暂停: P 键、难度: 简单、中等、困难三种模式

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>经典贪吃蛇 - 支持移动端</title> <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"> <script src="https://cdn.tailwindcss.com"></script> <script> tailwind.config = { theme: { extend: { colors: { primary: '#165DFF', secondary: '#FF7D00', dark: '#333333', light: '#F5F7FA', success: '#00B42A', danger: '#F53F3F', warning: '#FF7D00', info: '#86909C', snake: '#00B96B', food: '#FF7D00', special: '#FFD700' }, fontFamily: { inter: ['Inter', 'sans-serif'], }, } } } </script> <style> @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); body { touch-action: none; user-select: none; -webkit-tap-highlight-color: transparent; } .swipe-control { position: absolute; bottom: 20px; width: 180px; height: 180px; border-radius: 50%%; background: rgba(255, 255, 255, 0.2); backdrop-filter: blur(10px); display: none; z-index: 20; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); } .swipe-center { position: absolute; top: 50%%; left: 50%%; transform: translate(-50%%, -50%%); width: 60px; height: 60px; background: rgba(255, 255, 255, 0.8); border-radius: 50%%; display: flex; align-items: center; justify-content: center; font-size: 24px; color: #333; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); } .swipe-direction { position: absolute; width: 50px; height: 50px; border-radius: 50%%; background: rgba(255, 255, 255, 0.7); display: flex; align-items: center; justify-content: center; font-size: 20px; color: #165DFF; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); } .swipe-up { top: 0; left: 50%%; transform: translateX(-50%%); } .swipe-right { top: 50%%; right: 0; transform: translateY(-50%%); } .swipe-down { bottom: 0; left: 50%%; transform: translateX(-50%%); } .swipe-left { top: 50%%; left: 0; transform: translateY(-50%%); } .snake-head { position: relative; } .snake-eyes { position: absolute; top: 5px; width: 4px; height: 4px; background: #FFF; border-radius: 50%%; } .eye-left { left: 5px; } .eye-right { right: 5px; } .menu-item { transition: all 0.2s ease; } .menu-item:hover { transform: translateY(-3px); box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1); } .menu-item:active { transform: translateY(1px); } .high-score-item { background: linear-gradient(90deg, rgba(22, 93, 255, 0.1) 0%%, rgba(255, 255, 255, 0) 100%%); border-left: 4px solid #165DFF; } @media (max-width: 768px) { .swipe-control { display: block; } .controls-info { display: none; } } </style> </head> <body class="bg-gradient-to-br from-light to-info min-h-screen font-inter text-dark flex flex-col items-center justify-center p-4"> <!-- 游戏容器 --> <div class="max-w-3xl w-full mx-auto flex flex-col items-center"> <!-- 游戏标题 --> <h1 class="text-[clamp(2rem,5vw,3rem)] font-bold text-primary mb-4 flex items-center"> <i class="fa fa-gamepad mr-3"></i>经典贪吃蛇 </h1> <!-- 游戏控制区 --> <div class="w-full flex flex-col md:flex-row items-center justify-between mb-4 gap-4"> <!-- 分数和最高分 --> <div class="flex items-center gap-4 bg-white/80 rounded-xl p-3 shadow-lg"> <div> <p class="text-sm text-info">分数</p> <p id="score" class="text-2xl font-bold text-primary">0</p> </div> <div class="h-10 w-px bg-info/20"></div> <div> <p class="text-sm text-info">最高分</p> <p id="highScore" class="text-2xl font-bold text-secondary">0</p> </div> </div> <!-- 游戏控制按钮 --> <div class="flex flex-wrap items-center gap-3"> <select id="difficulty" class="bg-white/80 border border-info/30 rounded-xl px-4 py-2 text-dark focus:outline-none focus:ring-2 focus:ring-primary/50 appearance-none pr-8 text-sm"> <option value="easy">简单 (200ms)</option> <option value="medium" selected>中等 (150ms)</option> <option value="hard">困难 (100ms)</option> </select> <button id="startBtn" class="bg-primary hover:bg-primary/90 text-white rounded-xl px-6 py-2.5 transition-all flex items-center"> <i class="fa fa-play mr-2"></i>开始游戏 </button> <button id="pauseBtn" class="bg-warning hover:bg-warning/90 text-white rounded-xl px-6 py-2.5 transition-all flex items-center hidden"> <i class="fa fa-pause mr-2"></i>暂停 </button> <button id="restartBtn" class="bg-secondary hover:bg-secondary/90 text-white rounded-xl px-6 py-2.5 transition-all flex items-center hidden"> <i class="fa fa-refresh mr-2"></i>再来一次 </button> </div> </div> <!-- 游戏区域 --> <div class="relative w-full aspect-square max-w-2xl bg-white/90 rounded-2xl shadow-xl overflow-hidden"> <canvas id="gameCanvas" class="w-full h-full"></canvas> <!-- 移动端滑动控制 --> <div> <div> <i class="fa fa-arrows-alt"></i> </div> <div class="swipe-direction swipe-up"> <i class="fa fa-arrow-up"></i> </div> <div class="swipe-direction swipe-right"> <i class="fa fa-arrow-right"></i> </div> <div class="swipe-direction swipe-down"> <i class="fa fa-arrow-down"></i> </div> <div class="swipe-direction swipe-left"> <i class="fa fa-arrow-left"></i> </div> </div> <!-- 游戏开始遮罩(主菜单) --> <div id="startScreen" class="absolute inset-0 bg-dark/70 backdrop-blur flex flex-col items-center justify-center z-10 p-4"> <h2 class="text-[clamp(1.5rem,3vw,2.5rem)] font-bold text-white mb-6 text-center">贪吃蛇大作战</h2> <div class="w-full max-w-xs flex flex-col gap-4"> <button id="playBtn" class="menu-item bg-primary hover:bg-primary/90 text-white rounded-xl px-8 py-4 text-lg flex items-center justify-center"> <i class="fa fa-play-circle mr-3"></i>开始游戏 </button> <button id="highScoresBtn" class="menu-item bg-secondary hover:bg-secondary/90 text-white rounded-xl px-8 py-4 text-lg flex items-center justify-center"> <i class="fa fa-trophy mr-3"></i>查看高分 </button> <button id="exitBtn" class="menu-item bg-info hover:bg-info/90 text-white rounded-xl px-8 py-4 text-lg flex items-center justify-center"> <i class="fa fa-sign-out mr-3"></i>退出游戏 </button> </div> <p class="text-white/70 mt-8 text-sm max-w-md text-center">使用方向键或 WASD 控制蛇的移动,吃到食物增长得分,不要撞到墙壁和自己的身体!</p> </div> <!-- 高分排行榜 --> <div id="highScoresScreen" class="absolute inset-0 bg-dark/70 backdrop-blur flex flex-col items-center justify-center z-10 hidden p-4"> <h2 class="text-[clamp(1.5rem,3vw,2.5rem)] font-bold text-white mb-6 text-center">高分排行榜</h2> <div class="w-full max-w-md bg-white/90 rounded-xl p-4 mb-6"> <div class="flex justify-between items-center mb-4"> <span class="text-info font-bold">排名</span> <span class="text-info font-bold">玩家</span> <span class="text-info font-bold">分数</span> <span class="text-info font-bold">日期</span> </div> <div id="highScoresList"> <!-- 高分记录将通过JS动态生成 --> </div> </div> <button id="backToMenuBtn" class="bg-primary hover:bg-primary/90 text-white rounded-xl px-8 py-3 text-lg flex items-center"> <i class="fa fa-arrow-left mr-2"></i>返回主菜单 </button> </div> <!-- 游戏暂停遮罩 --> <div id="pauseScreen" class="absolute inset-0 bg-dark/70 backdrop-blur flex flex-col items-center justify-center z-10 hidden"> <h2 class="text-[clamp(1.5rem,3vw,2.5rem)] font-bold text-white mb-8">游戏暂停</h2> <button id="resumeBtn" class="bg-primary hover:bg-primary/90 text-white rounded-xl px-8 py-3 text-lg flex items-center"> <i class="fa fa-play mr-2"></i>继续游戏 </button> </div> <!-- 游戏结束遮罩 --> <div id="gameOverScreen" class="absolute inset-0 bg-dark/70 backdrop-blur flex flex-col items-center justify-center z-10 hidden"> <h2 class="text-[clamp(1.5rem,3vw,2.5rem)] font-bold text-danger mb-2">游戏结束</h2> <p class="text-white/80 mb-2">你的得分: <span id="finalScore" class="font-bold text-primary">0</span></p> <p class="text-white/80 mb-6">最高分: <span id="finalHighScore" class="font-bold text-secondary">0</span></p> <button id="playAgainBtn" class="bg-primary hover:bg-primary/90 text-white rounded-xl px-8 py-3 text-lg flex items-center mb-3"> <i class="fa fa-refresh mr-2"></i>再玩一次 </button> <button id="backToMenuBtnFromGameOver" class="bg-info hover:bg-info/90 text-white rounded-xl px-8 py-3 text-lg flex items-center"> <i class="fa fa-home mr-2"></i>返回菜单 </button> </div> </div> <!-- 游戏说明 --> <div class="mt-6 w-full bg-white/80 rounded-xl p-4 shadow-lg"> <h3 class="font-bold text-lg mb-2 flex items-center"> <i class="fa fa-info-circle mr-2 text-primary"></i>游戏说明 </h3> <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div> <p class="text-sm mb-2"><span>控制:</span> 方向键 ↑↓←→ 或 WASD</p> <p class="text-sm mb-2"><span>暂停:</span> P 键</p> <p><span>难度:</span> 简单、中等、困难三种模式</p> </div> <div> <p class="text-sm mb-2"><span>目标:</span> 吃到食物,尽可能获得高分</p> <p class="text-sm mb-2"><span>规则:</span> 撞到墙壁或自身即游戏结束</p> <p><span>特殊食物:</span> 金色食物得3分,限时出现</p> </div> </div> <div class="controls-info mt-4"> <p class="text-sm text-center text-info/80">在移动设备上,使用屏幕上的方向按钮控制蛇的移动</p> </div> </div> </div> <!-- 页脚 --> <footer class="mt-8 text-center text-info/70 text-sm"> <p>© 2025 经典贪吃蛇 | 支持键盘和触摸控制</p> </footer> <script> // DOM 元素 const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); const scoreDisplay = document.getElementById('score'); const highScoreDisplay = document.getElementById('highScore'); const finalScoreDisplay = document.getElementById('finalScore'); const finalHighScoreDisplay = document.getElementById('finalHighScore'); const difficultySelect = document.getElementById('difficulty'); const startBtn = document.getElementById('startBtn'); const pauseBtn = document.getElementById('pauseBtn'); const restartBtn = document.getElementById('restartBtn'); const playBtn = document.getElementById('playBtn'); const resumeBtn = document.getElementById('resumeBtn'); const playAgainBtn = document.getElementById('playAgainBtn'); const backToMenuBtn = document.getElementById('backToMenuBtn'); const backToMenuBtnFromGameOver = document.getElementById('backToMenuBtnFromGameOver'); const startScreen = document.getElementById('startScreen'); const pauseScreen = document.getElementById('pauseScreen'); const gameOverScreen = document.getElementById('gameOverScreen'); const highScoresBtn = document.getElementById('highScoresBtn'); const highScoresScreen = document.getElementById('highScoresScreen'); const highScoresList = document.getElementById('highScoresList'); const exitBtn = document.getElementById('exitBtn'); // 滑动控制元素 const swipeControl = document.querySelector('.swipe-control'); const swipeDirections = document.querySelectorAll('.swipe-direction'); // 游戏配置 const gridSize = 20; let gameSpeed = 150; // 默认中等难度 let gameInterval; let isPaused = false; let gameActive = false; // 游戏状态 let snake = []; let food = {}; let specialFood = null; let specialFoodTimer = null; let direction = ''; let nextDirection = ''; let score = 0; let highScore = localStorage.getItem('snakeHighScore') || 0; let highScores = JSON.parse(localStorage.getItem('snakeHighScores')) || []; // 设置高分显示 highScoreDisplay.textContent = highScore; // 调整Canvas大小以适应容器 function resizeCanvas() { const container = canvas.parentElement; const size = Math.min(container.clientWidth, container.clientHeight); canvas.width = size; canvas.height = size; } // 初始化游戏 function initGame() { // 重置游戏状态 const center = Math.floor((canvas.width / gridSize) / 2); snake = [ {x: center, y: center}, {x: center - 1, y: center}, {x: center - 2, y: center} ]; direction = 'right'; nextDirection = 'right'; score = 0; scoreDisplay.textContent = score; // 设置游戏速度 handleDifficultyChange(); // 生成初始食物 generateFood(); generateSpecialFood(); // 显示游戏画面,隐藏遮罩 startScreen.classList.add('hidden'); pauseScreen.classList.add('hidden'); gameOverScreen.classList.add('hidden'); highScoresScreen.classList.add('hidden'); pauseBtn.classList.remove('hidden'); restartBtn.classList.remove('hidden'); startBtn.classList.add('hidden'); // 开始游戏循环 if (gameInterval) clearInterval(gameInterval); gameInterval = setInterval(gameLoop, gameSpeed); gameActive = true; } // 生成普通食物 function generateFood() { const gridWidth = canvas.width / gridSize; const gridHeight = canvas.height / gridSize; // 随机位置 let newFood; do { newFood = { x: Math.floor(Math.random() * gridWidth), y: Math.floor(Math.random() * gridHeight) }; } while (isPositionOnSnake(newFood) || (specialFood && newFood.x === specialFood.x && newFood.y === specialFood.y)); food = newFood; } // 生成特殊食物 function generateSpecialFood() { // 清除之前的计时器和特殊食物 if (specialFoodTimer) clearTimeout(specialFoodTimer); specialFood = null; // 随机决定是否生成特殊食物 if (Math.random() < 0.3) { // 30%%概率生成 const gridWidth = canvas.width / gridSize; const gridHeight = canvas.height / gridSize; let newSpecialFood; do { newSpecialFood = { x: Math.floor(Math.random() * gridWidth), y: Math.floor(Math.random() * gridHeight) }; } while (isPositionOnSnake(newSpecialFood) || (newSpecialFood.x === food.x && newSpecialFood.y === food.y)); specialFood = newSpecialFood; // 特殊食物10秒后消失 specialFoodTimer = setTimeout(() => { specialFood = null; // 3-8秒后尝试再次生成 setTimeout(generateSpecialFood, Math.random() * 5000 + 3000); }, 10000); } else { // 3-8秒后尝试生成 setTimeout(generateSpecialFood, Math.random() * 5000 + 3000); } } // 检查位置是否在蛇身上 function isPositionOnSnake(position) { return snake.some(segment => segment.x === position.x && segment.y === position.y); } // 游戏主循环 function gameLoop() { if (isPaused || !gameActive) return; // 更新方向 direction = nextDirection; // 移动蛇 const head = {x: snake[0].x, y: snake[0].y}; switch(direction) { case 'up': head.y -= 1; break; case 'down': head.y += 1; break; case 'left': head.x -= 1; break; case 'right': head.x += 1; break; } // 检查碰撞 if (checkCollision(head)) { gameOver(); return; } // 检查是否吃到食物 let ate = false; if (head.x === food.x && head.y === food.y) { score += 1; scoreDisplay.textContent = score; generateFood(); ate = true; updateGameSpeed(); } // 检查是否吃到特殊食物 if (specialFood && head.x === specialFood.x && head.y === specialFood.y) { score += 3; scoreDisplay.textContent = score; clearTimeout(specialFoodTimer); generateSpecialFood(); ate = true; updateGameSpeed(); } // 添加新头部 snake.unshift(head); // 如果没吃到食物,移除尾部 if (!ate) { snake.pop(); } // 绘制游戏 draw(); } // 根据分数更新游戏速度 function updateGameSpeed() { const baseSpeed = difficultySelect.value === 'easy' ? 200 : difficultySelect.value === 'medium' ? 150 : 100; // 每得10分速度增加10%% const speedFactor = Math.min(0.5, Math.floor(score / 10) * 0.1); const newSpeed = Math.max(50, baseSpeed * (1 - speedFactor)); if (newSpeed !== gameSpeed) { gameSpeed = newSpeed; // 重新设置游戏循环 clearInterval(gameInterval); gameInterval = setInterval(gameLoop, gameSpeed); } } // 检查碰撞 function checkCollision(head) { const gridWidth = canvas.width / gridSize; const gridHeight = canvas.height / gridSize; // 检查是否撞到墙壁 if (head.x < 0 || head.x >= gridWidth || head.y < 0 || head.y >= gridHeight) { return true; } // 检查是否撞到自己 for (let i = 1; i < snake.length; i++) { if (head.x === snake[i].x && head.y === snake[i].y) { return true; } } return false; } // 绘制游戏 function draw() { // 清空画布 ctx.fillStyle = '#F5F7FA'; ctx.fillRect(0, 0, canvas.width, canvas.height); // 绘制网格(可选) ctx.strokeStyle = '#E5E9F2'; ctx.lineWidth = 0.5; for (let x = 0; x < canvas.width; x += gridSize) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, canvas.height); ctx.stroke(); } for (let y = 0; y < canvas.height; y += gridSize) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(canvas.width, y); ctx.stroke(); } // 绘制蛇 snake.forEach((segment, index) => { // 蛇头 if (index === 0) { ctx.fillStyle = '#00B96B'; ctx.shadowColor = 'rgba(0, 185, 107, 0.5)'; ctx.shadowBlur = 10; // 绘制头部 ctx.beginPath(); ctx.arc( segment.x * gridSize + gridSize / 2, segment.y * gridSize + gridSize / 2, gridSize / 2 - 1, 0, Math.PI * 2 ); ctx.fill(); // 绘制眼睛 ctx.fillStyle = 'white'; let eyeOffsetX = 0; let eyeOffsetY = 0; if (direction === 'right') { eyeOffsetX = gridSize / 4; ctx.beginPath(); ctx.arc( segment.x * gridSize + gridSize / 2 + eyeOffsetX - gridSize / 8, segment.y * gridSize + gridSize / 2 - gridSize / 8, gridSize / 8, 0, Math.PI * 2 ); ctx.fill(); ctx.beginPath(); ctx.arc( segment.x * gridSize + gridSize / 2 + eyeOffsetX - gridSize / 8, segment.y * gridSize + gridSize / 2 + gridSize / 8, gridSize / 8, 0, Math.PI * 2 ); ctx.fill(); } else if (direction === 'left') { eyeOffsetX = -gridSize / 4; ctx.beginPath(); ctx.arc( segment.x * gridSize + gridSize / 2 + eyeOffsetX + gridSize / 8, segment.y * gridSize + gridSize / 2 - gridSize / 8, gridSize / 8, 0, Math.PI * 2 ); ctx.fill(); ctx.beginPath(); ctx.arc( segment.x * gridSize + gridSize / 2 + eyeOffsetX + gridSize / 8, segment.y * gridSize + gridSize / 2 + gridSize / 8, gridSize / 8, 0, Math.PI * 2 ); ctx.fill(); } else if (direction === 'up') { eyeOffsetY = -gridSize / 4; ctx.beginPath(); ctx.arc( segment.x * gridSize + gridSize / 2 - gridSize / 8, segment.y * gridSize + gridSize / 2 + eyeOffsetY + gridSize / 8, gridSize / 8, 0, Math.PI * 2 ); ctx.fill(); ctx.beginPath(); ctx.arc( segment.x * gridSize + gridSize / 2 + gridSize / 8, segment.y * gridSize + gridSize / 2 + eyeOffsetY + gridSize / 8, gridSize / 8, 0, Math.PI * 2 ); ctx.fill(); } else if (direction === 'down') { eyeOffsetY = gridSize / 4; ctx.beginPath(); ctx.arc( segment.x * gridSize + gridSize / 2 - gridSize / 8, segment.y * gridSize + gridSize / 2 + eyeOffsetY - gridSize / 8, gridSize / 8, 0, Math.PI * 2 ); ctx.fill(); ctx.beginPath(); ctx.arc( segment.x * gridSize + gridSize / 2 + gridSize / 8, segment.y * gridSize + gridSize / 2 + eyeOffsetY - gridSize / 8, gridSize / 8, 0, Math.PI * 2 ); ctx.fill(); } ctx.shadowBlur = 0; } // 蛇身体渐变 else { // 根据位置设置渐变颜色 const colorPos = 1 - index / (snake.length + 5); const r = Math.floor(0 + (185 - 0) * colorPos); const g = Math.floor(185 + (107 - 185) * colorPos); const b = Math.floor(107 + (0 - 107) * colorPos); ctx.fillStyle = `rgb(${r}, ${g}, ${b})`; // 绘制圆角矩形身体 const radius = gridSize / 5; const x = segment.x * gridSize + 0.5; const y = segment.y * gridSize + 0.5; ctx.beginPath(); ctx.moveTo(x + radius, y); ctx.lineTo(x + gridSize - radius, y); ctx.arcTo(x + gridSize, y, x + gridSize, y + radius, radius); ctx.lineTo(x + gridSize, y + gridSize - radius); ctx.arcTo(x + gridSize, y + gridSize, x + gridSize - radius, y + gridSize, radius); ctx.lineTo(x + radius, y + gridSize); ctx.arcTo(x, y + gridSize, x, y + gridSize - radius, radius); ctx.lineTo(x, y + radius); ctx.arcTo(x, y, x + radius, y, radius); ctx.closePath(); ctx.fill(); // 身体高光 ctx.fillStyle = 'rgba(255, 255, 255, 0.2)'; ctx.beginPath(); ctx.ellipse( x + gridSize / 2, y + gridSize / 3, gridSize / 4, gridSize / 8, 0, 0, Math.PI * 2 ); ctx.fill(); } }); // 绘制食物 ctx.fillStyle = '#FF7D00'; ctx.shadowColor = 'rgba(255, 125, 0, 0.5)'; ctx.shadowBlur = 10; // 绘制苹果形状的食物 ctx.beginPath(); ctx.arc( food.x * gridSize + gridSize / 2, food.y * gridSize + gridSize / 2, gridSize / 2 - 1, 0, Math.PI * 2 ); ctx.fill(); // 苹果柄 ctx.fillStyle = '#8B4513'; ctx.fillRect( food.x * gridSize + gridSize / 2 - 1, food.y * gridSize - 2, 2, 6 ); ctx.shadowBlur = 0; // 绘制特殊食物(如果存在) if (specialFood) { ctx.fillStyle = '#FFD700'; ctx.shadowColor = 'rgba(255, 215, 0, 0.7)'; ctx.shadowBlur = 15; // 绘制闪烁效果 const flashRate = 500; // 闪烁频率 const flash = Math.floor(Date.now() / flashRate) %% 2 === 0; if (flash) { // 绘制星形特殊食物 drawStar( specialFood.x * gridSize + gridSize / 2, specialFood.y * gridSize + gridSize / 2, 5, gridSize / 2 - 1, gridSize / 4 ); } ctx.shadowBlur = 0; } } // 绘制星形 function drawStar(cx, cy, spikes, outerRadius, innerRadius) { let rot = Math.PI / 2 * 3; let x = cx; let y = cy; let step = Math.PI / spikes; ctx.beginPath(); ctx.moveTo(cx, cy - outerRadius); for (let i = 0; i < spikes; i++) { x = cx + Math.cos(rot) * outerRadius; y = cy + Math.sin(rot) * outerRadius; ctx.lineTo(x, y); rot += step; x = cx + Math.cos(rot) * innerRadius; y = cy + Math.sin(rot) * innerRadius; ctx.lineTo(x, y); rot += step; } ctx.lineTo(cx, cy - outerRadius); ctx.closePath(); ctx.fill(); } // 游戏暂停 function pauseGame() { isPaused = true; pauseScreen.classList.remove('hidden'); pauseBtn.innerHTML = '<i class="fa fa-play mr-2"></i>继续'; } // 游戏继续 function resumeGame() { isPaused = false; pauseScreen.classList.add('hidden'); pauseBtn.innerHTML = '<i class="fa fa-pause mr-2"></i>暂停'; } // 游戏结束 function gameOver() { gameActive = false; clearInterval(gameInterval); // 更新最高分 if (score > highScore) { highScore = score; localStorage.setItem('snakeHighScore', highScore); highScoreDisplay.textContent = highScore; } // 添加高分记录 addHighScore(score); // 显示游戏结束画面 finalScoreDisplay.textContent = score; finalHighScoreDisplay.textContent = highScore; gameOverScreen.classList.remove('hidden'); pauseBtn.classList.add('hidden'); restartBtn.classList.add('hidden'); startBtn.classList.remove('hidden'); } // 添加高分记录 function addHighScore(score) { // 获取当前日期 const now = new Date(); const dateStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`; // 添加新记录 highScores.push({ score: score, date: dateStr, player: '玩家' }); // 排序(高分在前) highScores.sort((a, b) => b.score - a.score); // 只保留前10名 if (highScores.length > 10) { highScores = highScores.slice(0, 10); } // 保存到本地存储 localStorage.setItem('snakeHighScores', JSON.stringify(highScores)); } // 渲染高分记录 function renderHighScores() { highScoresList.innerHTML = ''; if (highScores.length === 0) { highScoresList.innerHTML = '<p class="text-center py-4 text-info">暂无高分记录</p>'; return; } highScores.forEach((item, index) => { const scoreItem = document.createElement('div'); scoreItem.className = 'flex justify-between items-center py-2 px-3 rounded-lg high-score-item'; scoreItem.innerHTML = ` <span class="font-bold text-dark">${index + 1}</span> <span>${item.player}</span> <span class="font-bold text-secondary">${item.score}</span> <span class="text-sm text-info">${item.date}</span> `; highScoresList.appendChild(scoreItem); }); } // 切换暂停状态 function togglePause() { if (!gameActive) return; if (isPaused) { resumeGame(); } else { pauseGame(); } } // 处理键盘输入 function handleKeydown(e) { // 如果游戏未激活,不处理方向键 if (!gameActive && e.key !== 'p' && e.key !== 'P') return; switch(e.key) { case 'ArrowUp': case 'w': case 'W': if (direction !== 'down') { nextDirection = 'up'; } break; case 'ArrowDown': case 's': case 'S': if (direction !== 'up') { nextDirection = 'down'; } break; case 'ArrowLeft': case 'a': case 'A': if (direction !== 'right') { nextDirection = 'left'; } break; case 'ArrowRight': case 'd': case 'D': if (direction !== 'left') { nextDirection = 'right'; } break; case 'p': case 'P': togglePause(); break; case ' ': // 空格键开始游戏 if (!gameActive) { initGame(); } break; } } // 处理难度变化 function handleDifficultyChange() { const selectedDifficulty = difficultySelect.value; switch(selectedDifficulty) { case 'easy': gameSpeed = 200; break; case 'medium': gameSpeed = 150; break; case 'hard': gameSpeed = 100; break; } // 如果游戏正在进行,更新游戏速度 if (gameActive && !isPaused) { clearInterval(gameInterval); gameInterval = setInterval(gameLoop, gameSpeed); } } // 添加触摸事件处理 function setupTouchControls() { // 为每个方向按钮添加触摸事件 swipeDirections.forEach(button => { button.addEventListener('touchstart', (e) => { e.preventDefault(); if (!gameActive || isPaused) return; const direction = button.classList[1].split('-')[1]; if (direction === 'up' && nextDirection !== 'down') { nextDirection = 'up'; } else if (direction === 'down' && nextDirection !== 'up') { nextDirection = 'down'; } else if (direction === 'left' && nextDirection !== 'right') { nextDirection = 'left'; } else if (direction === 'right' && nextDirection !== 'left') { nextDirection = 'right'; } // 添加点击反馈 button.style.transform = 'scale(0.9)'; setTimeout(() => { button.style.transform = ''; }, 100); }); }); // 添加滑动手势支持 let touchStartX = 0; let touchStartY = 0; canvas.addEventListener('touchstart', (e) => { if (!gameActive || isPaused) return; const touch = e.touches[0]; touchStartX = touch.clientX; touchStartY = touch.clientY; }, { passive: false }); canvas.addEventListener('touchmove', (e) => { e.preventDefault(); }, { passive: false }); canvas.addEventListener('touchend', (e) => { if (!gameActive || isPaused) return; const touch = e.changedTouches[0]; const touchEndX = touch.clientX; const touchEndY = touch.clientY; const dx = touchEndX - touchStartX; const dy = touchEndY - touchStartY; // 确定滑动方向 if (Math.abs(dx) > Math.abs(dy)) { // 水平滑动 if (dx > 20 && nextDirection !== 'left') { nextDirection = 'right'; } else if (dx < -20 && nextDirection !== 'right') { nextDirection = 'left'; } } else { // 垂直滑动 if (dy > 20 && nextDirection !== 'up') { nextDirection = 'down'; } else if (dy < -20 && nextDirection !== 'down') { nextDirection = 'up'; } } }); } // 事件监听 window.addEventListener('keydown', handleKeydown); startBtn.addEventListener('click', initGame); pauseBtn.addEventListener('click', togglePause); restartBtn.addEventListener('click', initGame); playBtn.addEventListener('click', initGame); resumeBtn.addEventListener('click', resumeGame); playAgainBtn.addEventListener('click', initGame); backToMenuBtn.addEventListener('click', () => { highScoresScreen.classList.add('hidden'); startScreen.classList.remove('hidden'); }); backToMenuBtnFromGameOver.addEventListener('click', () => { gameOverScreen.classList.add('hidden'); startScreen.classList.remove('hidden'); }); difficultySelect.addEventListener('change', handleDifficultyChange); highScoresBtn.addEventListener('click', () => { startScreen.classList.add('hidden'); highScoresScreen.classList.remove('hidden'); renderHighScores(); }); exitBtn.addEventListener('click', () => { if (confirm('确定要退出游戏吗?')) { window.close(); } }); // 窗口大小改变时调整Canvas window.addEventListener('resize', () => { const wasActive = gameActive; const wasPaused = isPaused; if (gameActive) { clearInterval(gameInterval); gameActive = false; } resizeCanvas(); if (wasActive) { gameActive = true; if (!wasPaused) { gameInterval = setInterval(gameLoop, gameSpeed); } } // 重新绘制游戏 if (snake.length > 0) { draw(); } }); // 初始化 resizeCanvas(); draw(); setupTouchControls(); </script> </body> </html>
本文最后更新时间 2025-06-01
文章链接地址:https://xzlo.blog/index.php/archives/47/
本站文章除注明[转载|引用|原文]出处外,均为本站原生内容,转载前请注明出处