​经典贪吃蛇游戏

经典贪吃蛇

纯前端代码实现经典贪吃蛇游戏,含电脑端方向键控制,暂停,难度模式选择,以及特殊食物和保存历史最高分等功能。新增支持移动端控制

控制: 方向键 ↑↓←→ 或 WASD、暂停: P 键、难度: 简单、中等、困难三种模式

e9c50dab2fec9f786edbcfc55f414c7.png
<!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/
本站文章除注明[转载|引用|原文]出处外,均为本站原生内容,转载前请注明出处

留言