Download index.html from erdes/snake: direct link, hf CLI and curl.
- Browser
- Download file 39.9 kB
-
https://huggingface.co/spaces/erdes/snake/resolve/main/index.html
- Command line
-
hf download hf://spaces/erdes/snake/index.html
-
curl -L -o index.html https://huggingface.co/spaces/erdes/snake/resolve/main/index.html
39.9 kB
| <html lang="zh-CN"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>贪吃蛇游戏 - Snake Customizer</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap'); | |
| :root { | |
| --grid-size: 20px; | |
| --grid-color: #374151; | |
| --snake-color: #10B981; | |
| --food-color: #EF4444; | |
| --obstacle-color: #6B7280; | |
| --bg-color: #111827; | |
| --speed: 150; | |
| } | |
| .game-container { | |
| position: relative; | |
| background-color: var(--bg-color); | |
| background-image: | |
| linear-gradient(var(--grid-color) 1px, transparent 1px), | |
| linear-gradient(90deg, var(--grid-color) 1px, transparent 1px); | |
| background-size: var(--grid-size) var(--grid-size); | |
| } | |
| .snake-head { | |
| background-color: var(--snake-color); | |
| border-radius: 4px; | |
| box-shadow: 0 0 5px rgba(16, 185, 129, 0.7); | |
| } | |
| .snake-body { | |
| background-color: var(--snake-color); | |
| border-radius: 2px; | |
| } | |
| .food { | |
| background-color: var(--food-color); | |
| border-radius: 50%; | |
| box-shadow: 0 0 8px rgba(239, 68, 68, 0.8); | |
| animation: pulse 1.5s infinite; | |
| } | |
| .obstacle { | |
| background-color: var(--obstacle-color); | |
| border-radius: 3px; | |
| } | |
| .retro-font { | |
| font-family: 'Press Start 2P', cursive; | |
| } | |
| .custom-slider::-webkit-slider-thumb { | |
| -webkit-appearance: none; | |
| appearance: none; | |
| width: 20px; | |
| height: 20px; | |
| border-radius: 50%; | |
| background: #10B981; | |
| cursor: pointer; | |
| } | |
| .custom-slider::-moz-range-thumb { | |
| width: 20px; | |
| height: 20px; | |
| border-radius: 50%; | |
| background: #10B981; | |
| cursor: pointer; | |
| } | |
| .color-preview { | |
| width: 30px; | |
| height: 30px; | |
| border-radius: 4px; | |
| cursor: pointer; | |
| } | |
| .settings-panel { | |
| transition: transform 0.3s ease-in-out; | |
| } | |
| .settings-panel.hidden { | |
| transform: translateX(100%); | |
| } | |
| .game-over { | |
| background: rgba(0, 0, 0, 0.8); | |
| backdrop-filter: blur(4px); | |
| } | |
| @keyframes pulse { | |
| 0% { transform: scale(1); } | |
| 50% { transform: scale(1.1); } | |
| 100% { transform: scale(1); } | |
| } | |
| @keyframes snakeMove { | |
| 0% { transform: scale(0.9); } | |
| 50% { transform: scale(1); } | |
| 100% { transform: scale(0.9); } | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-900 text-gray-100 min-h-screen flex flex-col"> | |
| <!-- 标题区域 --> | |
| <header class="py-4 px-6 bg-gray-800 border-b border-green-500"> | |
| <div class="container mx-auto flex justify-between items-center"> | |
| <h1 class="text-2xl md:text-3xl font-bold text-green-400 retro-font flex items-center"> | |
| <i class="fas fa-snake mr-3"></i>贪吃蛇游戏 - Snake Customizer | |
| </h1> | |
| <div class="flex items-center space-x-2"> | |
| <span id="score-display" class="bg-gray-700 px-4 py-2 rounded-lg text-green-300 retro-font">分数: 0</span> | |
| <button id="fullscreen-btn" class="bg-gray-600 hover:bg-gray-500 text-white p-2 rounded-lg transition" title="全屏"> | |
| <i class="fas fa-expand"></i> | |
| </button> | |
| <button id="settings-toggle" class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-lg transition"> | |
| <i class="fas fa-cog"></i> 设置 | |
| </button> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- 主内容区 --> | |
| <main class="flex-grow flex flex-col md:flex-row container mx-auto px-4 py-6 gap-6"> | |
| <!-- 游戏区域 --> | |
| <div class="flex-grow flex flex-col items-center"> | |
| <div class="game-container w-full max-w-2xl h-96 md:h-[500px] rounded-xl overflow-hidden border-2 border-green-500 relative" id="game-canvas"> | |
| <!-- 游戏结束提示 --> | |
| <div id="game-over" class="game-over absolute inset-0 flex flex-col items-center justify-center hidden"> | |
| <h2 class="text-3xl font-bold text-red-500 mb-4 retro-font">游戏结束!</h2> | |
| <p class="text-xl text-green-300 mb-6 retro-font">最终分数: <span id="final-score">0</span></p> | |
| <button id="restart-btn" class="bg-green-600 hover:bg-green-700 text-white px-6 py-3 rounded-lg text-xl transition"> | |
| <i class="fas fa-redo mr-2"></i>重新开始 | |
| </button> | |
| </div> | |
| </div> | |
| <!-- 控制按钮 --> | |
| <div class="mt-6 flex flex-wrap justify-center gap-4"> | |
| <button id="start-btn" class="bg-green-600 hover:bg-green-700 text-white px-6 py-3 rounded-lg text-lg transition"> | |
| <i class="fas fa-play mr-2"></i>开始游戏 | |
| </button> | |
| <button id="pause-btn" class="bg-yellow-600 hover:bg-yellow-700 text-white px-6 py-3 rounded-lg text-lg transition"> | |
| <i class="fas fa-pause mr-2"></i>暂停 | |
| </button> | |
| <button id="reset-btn" class="bg-red-600 hover:bg-red-700 text-white px-6 py-3 rounded-lg text-lg transition"> | |
| <i class="fas fa-sync mr-2"></i>重置 | |
| </button> | |
| </div> | |
| <!-- 移动设备控制 --> | |
| <div class="mt-6 grid grid-cols-3 gap-2 w-64"> | |
| <div></div> | |
| <button id="up-btn" class="bg-gray-700 hover:bg-gray-600 text-white p-4 rounded-lg text-center"> | |
| <i class="fas fa-arrow-up"></i> | |
| </button> | |
| <div></div> | |
| <button id="left-btn" class="bg-gray-700 hover:bg-gray-600 text-white p-4 rounded-lg text-center"> | |
| <i class="fas fa-arrow-left"></i> | |
| </button> | |
| <div></div> | |
| <button id="right-btn" class="bg-gray-700 hover:bg-gray-600 text-white p-4 rounded-lg text-center"> | |
| <i class="fas fa-arrow-right"></i> | |
| </button> | |
| <div></div> | |
| <button id="down-btn" class="bg-gray-700 hover:bg-gray-600 text-white p-4 rounded-lg text-center"> | |
| <i class="fas fa-arrow-down"></i> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 设置面板 --> | |
| <div id="settings-panel" class="settings-panel w-full md:w-80 bg-gray-800 rounded-xl p-6 border border-green-500 h-fit"> | |
| <div class="flex justify-between items-center mb-6"> | |
| <h2 class="text-xl font-bold text-green-400"><i class="fas fa-sliders-h mr-2"></i>游戏设置</h2> | |
| <button id="close-settings" class="text-gray-400 hover:text-white"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="space-y-6"> | |
| <!-- 难度设置 --> | |
| <div> | |
| <h3 class="text-lg font-semibold mb-2 text-gray-300"><i class="fas fa-tachometer-alt mr-2"></i>难度设置</h3> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block mb-1 text-gray-400">游戏速度</label> | |
| <div class="flex items-center"> | |
| <span class="mr-2 text-sm">慢</span> | |
| <input type="range" min="50" max="300" value="150" class="custom-slider w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer" id="speed-slider"> | |
| <span class="ml-2 text-sm">快</span> | |
| </div> | |
| <div class="text-right text-sm text-green-400" id="speed-value">中等</div> | |
| </div> | |
| <div> | |
| <label class="block mb-1 text-gray-400">网格大小</label> | |
| <div class="flex gap-2"> | |
| <button data-size="15" class="grid-size-btn flex-1 bg-gray-700 hover:bg-gray-600 py-2 rounded">小</button> | |
| <button data-size="20" class="grid-size-btn flex-1 bg-green-600 py-2 rounded">中</button> | |
| <button data-size="25" class="grid-size-btn flex-1 bg-gray-700 hover:bg-gray-600 py-2 rounded">大</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 游戏模式 --> | |
| <div> | |
| <h3 class="text-lg font-semibold mb-2 text-gray-300"><i class="fas fa-gamepad mr-2"></i>游戏模式</h3> | |
| <div class="space-y-2"> | |
| <label class="flex items-center cursor-pointer"> | |
| <input type="checkbox" id="wall-collision" class="form-checkbox h-5 w-5 text-green-500" checked> | |
| <span class="ml-2 text-gray-300">墙壁碰撞</span> | |
| </label> | |
| <label class="flex items-center cursor-pointer"> | |
| <input type="checkbox" id="obstacles" class="form-checkbox h-5 w-5 text-green-500"> | |
| <span class="ml-2 text-gray-300">添加障碍物</span> | |
| </label> | |
| <label class="flex items-center cursor-pointer"> | |
| <input type="checkbox" id="grow-mode" class="form-checkbox h-5 w-5 text-green-500"> | |
| <span class="ml-2 text-gray-300">成长模式</span> | |
| </label> | |
| </div> | |
| </div> | |
| <!-- 外观设置 --> | |
| <div> | |
| <h3 class="text-lg font-semibold mb-2 text-gray-300"><i class="fas fa-palette mr-2"></i>外观设置</h3> | |
| <div class="grid grid-cols-2 gap-4"> | |
| <div> | |
| <label class="block mb-1 text-gray-400">蛇的颜色</label> | |
| <div class="flex gap-2"> | |
| <div class="color-preview bg-green-500" data-color="#10B981" title="绿色"></div> | |
| <div class="color-preview bg-blue-500" data-color="#3B82F6" title="蓝色"></div> | |
| <div class="color-preview bg-purple-500" data-color="#8B5CF6" title="紫色"></div> | |
| </div> | |
| </div> | |
| <div> | |
| <label class="block mb-1 text-gray-400">食物颜色</label> | |
| <div class="flex gap-2"> | |
| <div class="color-preview bg-red-500" data-color="#EF4444" title="红色"></div> | |
| <div class="color-preview bg-yellow-500" data-color="#F59E0B" title="黄色"></div> | |
| <div class="color-preview bg-pink-500" data-color="#EC4899" title="粉色"></div> | |
| </div> | |
| </div> | |
| <div> | |
| <label class="block mb-1 text-gray-400">网格颜色</label> | |
| <div class="flex gap-2"> | |
| <div class="color-preview bg-gray-500" data-color="#6B7280" title="灰色"></div> | |
| <div class="color-preview bg-blue-400" data-color="#60A5FA" title="浅蓝"></div> | |
| <div class="color-preview bg-green-400" data-color="#34D399" title="浅绿"></div> | |
| </div> | |
| </div> | |
| <div> | |
| <label class="block mb-1 text-gray-400">背景颜色</label> | |
| <div class="flex gap-2"> | |
| <div class="color-preview bg-gray-900" data-color="#111827" title="深灰"></div> | |
| <div class="color-preview bg-gray-800" data-color="#1F2937" title="中灰"></div> | |
| <div class="color-preview bg-gray-700" data-color="#374151" title="浅灰"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 高级设置 --> | |
| <div> | |
| <h3 class="text-lg font-semibold mb-2 text-gray-300"><i class="fas fa-cogs mr-2"></i>高级设置</h3> | |
| <div class="space-y-2"> | |
| <div> | |
| <label class="block mb-1 text-gray-400">初始长度</label> | |
| <input type="range" min="3" max="10" value="4" class="custom-slider w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer" id="initial-length"> | |
| <div class="text-right text-sm text-green-400" id="length-value">4</div> | |
| </div> | |
| <div> | |
| <label class="block mb-1 text-gray-400">食物数量</label> | |
| <input type="range" min="1" max="5" value="1" class="custom-slider w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer" id="food-count"> | |
| <div class="text-right text-sm text-green-400" id="food-count-value">1</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <button id="apply-settings" class="mt-8 w-full bg-green-600 hover:bg-green-700 text-white py-3 rounded-lg text-lg transition"> | |
| <i class="fas fa-check-circle mr-2"></i>应用设置 | |
| </button> | |
| </div> | |
| </main> | |
| <!-- 游戏说明 --> | |
| <footer class="bg-gray-800 border-t border-green-500 py-6 px-4"> | |
| <div class="container mx-auto"> | |
| <h2 class="text-xl font-bold text-green-400 mb-4"><i class="fas fa-info-circle mr-2"></i>游戏说明</h2> | |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-4 text-gray-300"> | |
| <div class="bg-gray-700 p-4 rounded-lg"> | |
| <h3 class="font-semibold text-green-300 mb-2"><i class="fas fa-keyboard mr-2"></i>控制方式</h3> | |
| <p>键盘方向键或WASD控制蛇的移动方向</p> | |
| <p>移动设备可使用屏幕上的方向按钮</p> | |
| </div> | |
| <div class="bg-gray-700 p-4 rounded-lg"> | |
| <h3 class="font-semibold text-green-300 mb-2"><i class="fas fa-star mr-2"></i>游戏目标</h3> | |
| <p>控制蛇吃到食物(红色圆点)</p> | |
| <p>每吃到一个食物,蛇身长度增加,分数增加</p> | |
| </div> | |
| <div class="bg-gray-700 p-4 rounded-lg"> | |
| <h3 class="font-semibold text-green-300 mb-2"><i class="fas fa-skull mr-2"></i>游戏结束</h3> | |
| <p>蛇撞到墙壁(如果开启墙壁碰撞)</p> | |
| <p>蛇撞到自己的身体</p> | |
| <p>蛇撞到障碍物(如果开启障碍物)</p> | |
| </div> | |
| </div> | |
| </div> | |
| </footer> | |
| <script> | |
| // 游戏状态变量 | |
| let game = { | |
| canvas: null, | |
| ctx: null, | |
| gridSize: 20, | |
| width: 0, | |
| height: 0, | |
| snake: [], | |
| direction: 'right', | |
| nextDirection: 'right', | |
| food: [], | |
| obstacles: [], | |
| score: 0, | |
| isRunning: false, | |
| isPaused: false, | |
| gameLoop: null, | |
| speed: 150, | |
| wallCollision: true, | |
| obstaclesEnabled: false, | |
| growMode: false, | |
| initialLength: 4, | |
| foodCount: 1 | |
| }; | |
| // 颜色设置 | |
| const colors = { | |
| snake: '#10B981', | |
| food: '#EF4444', | |
| grid: '#374151', | |
| bg: '#111827', | |
| obstacle: '#6B7280' | |
| }; | |
| // DOM 元素 | |
| const elements = { | |
| gameContainer: document.getElementById('game-canvas'), | |
| fullscreenBtn: document.getElementById('fullscreen-btn'), | |
| scoreDisplay: document.getElementById('score-display'), | |
| gameOver: document.getElementById('game-over'), | |
| finalScore: document.getElementById('final-score'), | |
| restartBtn: document.getElementById('restart-btn'), | |
| startBtn: document.getElementById('start-btn'), | |
| pauseBtn: document.getElementById('pause-btn'), | |
| resetBtn: document.getElementById('reset-btn'), | |
| settingsPanel: document.getElementById('settings-panel'), | |
| settingsToggle: document.getElementById('settings-toggle'), | |
| closeSettings: document.getElementById('close-settings'), | |
| applySettings: document.getElementById('apply-settings'), | |
| speedSlider: document.getElementById('speed-slider'), | |
| speedValue: document.getElementById('speed-value'), | |
| wallCollision: document.getElementById('wall-collision'), | |
| obstacles: document.getElementById('obstacles'), | |
| growMode: document.getElementById('grow-mode'), | |
| initialLength: document.getElementById('initial-length'), | |
| lengthValue: document.getElementById('length-value'), | |
| foodCount: document.getElementById('food-count'), | |
| foodCountValue: document.getElementById('food-count-value'), | |
| gridSizeBtns: document.querySelectorAll('.grid-size-btn'), | |
| colorPreviews: document.querySelectorAll('.color-preview'), | |
| upBtn: document.getElementById('up-btn'), | |
| downBtn: document.getElementById('down-btn'), | |
| leftBtn: document.getElementById('left-btn'), | |
| rightBtn: document.getElementById('right-btn') | |
| }; | |
| // 初始化游戏 | |
| function initGame() { | |
| // 获取游戏区域尺寸 | |
| const rect = elements.gameContainer.getBoundingClientRect(); | |
| game.width = Math.floor(rect.width / game.gridSize) * game.gridSize; | |
| game.height = Math.floor(rect.height / game.gridSize) * game.gridSize; | |
| // 创建画布 | |
| game.canvas = document.createElement('canvas'); | |
| game.canvas.width = game.width; | |
| game.canvas.height = game.height; | |
| game.canvas.classList.add('absolute'); | |
| elements.gameContainer.innerHTML = ''; | |
| elements.gameContainer.appendChild(game.canvas); | |
| game.ctx = game.canvas.getContext('2d'); | |
| // 重置游戏状态 | |
| resetGame(); | |
| // 更新CSS变量 | |
| updateCSSVariables(); | |
| } | |
| // 重置游戏 | |
| function resetGame() { | |
| game.snake = []; | |
| game.food = []; | |
| game.obstacles = []; | |
| game.direction = 'right'; | |
| game.nextDirection = 'right'; | |
| game.score = 0; | |
| game.isRunning = false; | |
| game.isPaused = false; | |
| // 隐藏游戏结束画面 | |
| elements.gameOver.classList.add('hidden'); | |
| // 更新分数显示 | |
| updateScore(); | |
| // 创建初始蛇 | |
| createSnake(); | |
| // 创建食物 | |
| createFood(); | |
| // 创建障碍物(如果启用) | |
| if (game.obstaclesEnabled) { | |
| createObstacles(); | |
| } | |
| // 绘制游戏 | |
| drawGame(); | |
| } | |
| // 创建蛇 | |
| function createSnake() { | |
| game.snake = []; | |
| const startX = Math.floor(game.width / game.gridSize / 4) * game.gridSize; | |
| const startY = Math.floor(game.height / game.gridSize / 2) * game.gridSize; | |
| for (let i = 0; i < game.initialLength; i++) { | |
| game.snake.push({ | |
| x: startX - i * game.gridSize, | |
| y: startY | |
| }); | |
| } | |
| } | |
| // 创建食物 | |
| function createFood() { | |
| for (let i = 0; i < game.foodCount; i++) { | |
| let newFood; | |
| let overlapping; | |
| do { | |
| overlapping = false; | |
| newFood = { | |
| x: Math.floor(Math.random() * (game.width / game.gridSize)) * game.gridSize, | |
| y: Math.floor(Math.random() * (game.height / game.gridSize)) * game.gridSize | |
| }; | |
| // 检查是否与蛇重叠 | |
| for (const segment of game.snake) { | |
| if (segment.x === newFood.x && segment.y === newFood.y) { | |
| overlapping = true; | |
| break; | |
| } | |
| } | |
| // 检查是否与现有食物重叠 | |
| for (const food of game.food) { | |
| if (food.x === newFood.x && food.y === newFood.y) { | |
| overlapping = true; | |
| break; | |
| } | |
| } | |
| // 检查是否与障碍物重叠 | |
| for (const obstacle of game.obstacles) { | |
| if (obstacle.x === newFood.x && obstacle.y === newFood.y) { | |
| overlapping = true; | |
| break; | |
| } | |
| } | |
| } while (overlapping); | |
| game.food.push(newFood); | |
| } | |
| } | |
| // 创建障碍物 | |
| function createObstacles() { | |
| const obstacleCount = Math.floor((game.width / game.gridSize) * (game.height / game.gridSize) * 0.05); | |
| for (let i = 0; i < obstacleCount; i++) { | |
| let newObstacle; | |
| let overlapping; | |
| do { | |
| overlapping = false; | |
| newObstacle = { | |
| x: Math.floor(Math.random() * (game.width / game.gridSize)) * game.gridSize, | |
| y: Math.floor(Math.random() * (game.height / game.gridSize)) * game.gridSize | |
| }; | |
| // 检查是否与蛇重叠 | |
| for (const segment of game.snake) { | |
| if (segment.x === newObstacle.x && segment.y === newObstacle.y) { | |
| overlapping = true; | |
| break; | |
| } | |
| } | |
| // 检查是否与食物重叠 | |
| for (const food of game.food) { | |
| if (food.x === newObstacle.x && food.y === newObstacle.y) { | |
| overlapping = true; | |
| break; | |
| } | |
| } | |
| // 检查是否与其他障碍物重叠 | |
| for (const obstacle of game.obstacles) { | |
| if (obstacle.x === newObstacle.x && obstacle.y === newObstacle.y) { | |
| overlapping = true; | |
| break; | |
| } | |
| } | |
| } while (overlapping); | |
| game.obstacles.push(newObstacle); | |
| } | |
| } | |
| // 移动蛇 | |
| function moveSnake() { | |
| if (!game.isRunning || game.isPaused) return; | |
| // 更新方向 | |
| game.direction = game.nextDirection; | |
| // 计算新头部位置 | |
| const head = {...game.snake[0]}; | |
| switch (game.direction) { | |
| case 'up': | |
| head.y -= game.gridSize; | |
| break; | |
| case 'down': | |
| head.y += game.gridSize; | |
| break; | |
| case 'left': | |
| head.x -= game.gridSize; | |
| break; | |
| case 'right': | |
| head.x += game.gridSize; | |
| break; | |
| } | |
| // 检查墙壁碰撞 | |
| if (game.wallCollision) { | |
| if (head.x < 0 || head.x >= game.width || head.y < 0 || head.y >= game.height) { | |
| gameOver(); | |
| return; | |
| } | |
| } else { | |
| // 穿墙处理 | |
| if (head.x < 0) head.x = game.width - game.gridSize; | |
| if (head.x >= game.width) head.x = 0; | |
| if (head.y < 0) head.y = game.height - game.gridSize; | |
| if (head.y >= game.height) head.y = 0; | |
| } | |
| // 检查自身碰撞 | |
| for (let i = 0; i < game.snake.length; i++) { | |
| if (head.x === game.snake[i].x && head.y === game.snake[i].y) { | |
| gameOver(); | |
| return; | |
| } | |
| } | |
| // 检查障碍物碰撞 | |
| if (game.obstaclesEnabled) { | |
| for (const obstacle of game.obstacles) { | |
| if (head.x === obstacle.x && head.y === obstacle.y) { | |
| gameOver(); | |
| return; | |
| } | |
| } | |
| } | |
| // 添加新头部 | |
| game.snake.unshift(head); | |
| // 检查食物碰撞 | |
| let ateFood = false; | |
| for (let i = 0; i < game.food.length; i++) { | |
| if (head.x === game.food[i].x && head.y === game.food[i].y) { | |
| // 移除被吃的食物 | |
| game.food.splice(i, 1); | |
| // 增加分数 | |
| game.score += 10; | |
| updateScore(); | |
| // 创建新食物 | |
| createFood(); | |
| ateFood = true; | |
| break; | |
| } | |
| } | |
| // 如果没有吃到食物,移除尾部 | |
| if (!ateFood && !game.growMode) { | |
| game.snake.pop(); | |
| } | |
| // 绘制游戏 | |
| drawGame(); | |
| } | |
| // 绘制游戏 | |
| function drawGame() { | |
| // 清除画布 | |
| game.ctx.clearRect(0, 0, game.width, game.height); | |
| // 绘制蛇 | |
| for (let i = 0; i < game.snake.length; i++) { | |
| const segment = game.snake[i]; | |
| game.ctx.fillStyle = i === 0 ? colors.snake : colors.snake; | |
| game.ctx.fillRect(segment.x, segment.y, game.gridSize, game.gridSize); | |
| // 添加特殊样式 | |
| if (i === 0) { | |
| game.ctx.fillStyle = '#047857'; | |
| game.ctx.fillRect(segment.x + 2, segment.y + 2, game.gridSize - 4, game.gridSize - 4); | |
| } else { | |
| game.ctx.fillStyle = '#065F46'; | |
| game.ctx.fillRect(segment.x + 1, segment.y + 1, game.gridSize - 2, game.gridSize - 2); | |
| } | |
| } | |
| // 绘制食物 | |
| for (const food of game.food) { | |
| game.ctx.fillStyle = colors.food; | |
| game.ctx.beginPath(); | |
| game.ctx.arc(food.x + game.gridSize/2, food.y + game.gridSize/2, game.gridSize/2 - 1, 0, Math.PI * 2); | |
| game.ctx.fill(); | |
| // 添加高光 | |
| game.ctx.fillStyle = 'rgba(255, 255, 255, 0.3)'; | |
| game.ctx.beginPath(); | |
| game.ctx.arc(food.x + game.gridSize/3, food.y + game.gridSize/3, game.gridSize/6, 0, Math.PI * 2); | |
| game.ctx.fill(); | |
| } | |
| // 绘制障碍物 | |
| for (const obstacle of game.obstacles) { | |
| game.ctx.fillStyle = colors.obstacle; | |
| game.ctx.fillRect(obstacle.x, obstacle.y, game.gridSize, game.gridSize); | |
| // 添加纹理 | |
| game.ctx.fillStyle = '#4B5563'; | |
| game.ctx.fillRect(obstacle.x + 2, obstacle.y + 2, game.gridSize - 4, game.gridSize - 4); | |
| } | |
| } | |
| // 游戏结束 | |
| function gameOver() { | |
| game.isRunning = false; | |
| clearInterval(game.gameLoop); | |
| // 显示游戏结束画面 | |
| elements.finalScore.textContent = game.score; | |
| elements.gameOver.classList.remove('hidden'); | |
| } | |
| // 开始游戏 | |
| function startGame() { | |
| if (game.isRunning) return; | |
| game.isRunning = true; | |
| game.isPaused = false; | |
| // 开始游戏循环 | |
| game.gameLoop = setInterval(moveSnake, 300 - game.speed); | |
| } | |
| // 暂停游戏 | |
| function pauseGame() { | |
| if (!game.isRunning) return; | |
| game.isPaused = !game.isPaused; | |
| if (game.isPaused) { | |
| clearInterval(game.gameLoop); | |
| elements.pauseBtn.innerHTML = '<i class="fas fa-play mr-2"></i>继续'; | |
| } else { | |
| game.gameLoop = setInterval(moveSnake, 300 - game.speed); | |
| elements.pauseBtn.innerHTML = '<i class="fas fa-pause mr-2"></i>暂停'; | |
| } | |
| } | |
| // 更新分数显示 | |
| function updateScore() { | |
| elements.scoreDisplay.textContent = `分数: ${game.score}`; | |
| } | |
| // 更新CSS变量 | |
| function updateCSSVariables() { | |
| document.documentElement.style.setProperty('--grid-size', `${game.gridSize}px`); | |
| document.documentElement.style.setProperty('--grid-color', colors.grid); | |
| document.documentElement.style.setProperty('--snake-color', colors.snake); | |
| document.documentElement.style.setProperty('--food-color', colors.food); | |
| document.documentElement.style.setProperty('--obstacle-color', colors.obstacle); | |
| document.documentElement.style.setProperty('--bg-color', colors.bg); | |
| document.documentElement.style.setProperty('--speed', game.speed); | |
| } | |
| // 应用设置 | |
| function applySettings() { | |
| // 更新游戏设置 | |
| game.speed = parseInt(elements.speedSlider.value); | |
| game.wallCollision = elements.wallCollision.checked; | |
| game.obstaclesEnabled = elements.obstacles.checked; | |
| game.growMode = elements.growMode.checked; | |
| game.initialLength = parseInt(elements.initialLength.value); | |
| game.foodCount = parseInt(elements.foodCount.value); | |
| // 更新速度显示 | |
| if (game.speed < 100) { | |
| elements.speedValue.textContent = '慢'; | |
| } else if (game.speed < 200) { | |
| elements.speedValue.textContent = '中等'; | |
| } else { | |
| elements.speedValue.textContent = '快'; | |
| } | |
| // 更新长度显示 | |
| elements.lengthValue.textContent = game.initialLength; | |
| // 更新食物数量显示 | |
| elements.foodCountValue.textContent = game.foodCount; | |
| // 重新初始化游戏 | |
| initGame(); | |
| // 关闭设置面板 | |
| elements.settingsPanel.classList.add('hidden'); | |
| } | |
| // 事件监听器 | |
| function setupEventListeners() { | |
| // 键盘控制 | |
| document.addEventListener('keydown', (e) => { | |
| if (!game.isRunning) return; | |
| switch (e.key) { | |
| case 'ArrowUp': | |
| case 'w': | |
| case 'W': | |
| if (game.direction !== 'down') game.nextDirection = 'up'; | |
| break; | |
| case 'ArrowDown': | |
| case 's': | |
| case 'S': | |
| if (game.direction !== 'up') game.nextDirection = 'down'; | |
| break; | |
| case 'ArrowLeft': | |
| case 'a': | |
| case 'A': | |
| if (game.direction !== 'right') game.nextDirection = 'left'; | |
| break; | |
| case 'ArrowRight': | |
| case 'd': | |
| case 'D': | |
| if (game.direction !== 'left') game.nextDirection = 'right'; | |
| break; | |
| } | |
| }); | |
| // 按钮事件 | |
| elements.startBtn.addEventListener('click', startGame); | |
| elements.pauseBtn.addEventListener('click', pauseGame); | |
| elements.resetBtn.addEventListener('click', resetGame); | |
| elements.restartBtn.addEventListener('click', resetGame); | |
| // 设置面板事件 | |
| elements.settingsToggle.addEventListener('click', () => { | |
| elements.settingsPanel.classList.remove('hidden'); | |
| }); | |
| elements.closeSettings.addEventListener('click', () => { | |
| elements.settingsPanel.classList.add('hidden'); | |
| }); | |
| elements.applySettings.addEventListener('click', applySettings); | |
| // 网格大小按钮 | |
| elements.gridSizeBtns.forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| elements.gridSizeBtns.forEach(b => b.classList.remove('bg-green-600')); | |
| btn.classList.add('bg-green-600'); | |
| game.gridSize = parseInt(btn.dataset.size); | |
| initGame(); | |
| }); | |
| }); | |
| // 颜色选择 | |
| elements.colorPreviews.forEach(preview => { | |
| preview.addEventListener('click', () => { | |
| const color = preview.dataset.color; | |
| const role = preview.parentElement.querySelector('label').textContent; | |
| switch (role) { | |
| case '蛇的颜色': | |
| colors.snake = color; | |
| break; | |
| case '食物颜色': | |
| colors.food = color; | |
| break; | |
| case '网格颜色': | |
| colors.grid = color; | |
| break; | |
| case '背景颜色': | |
| colors.bg = color; | |
| break; | |
| } | |
| updateCSSVariables(); | |
| drawGame(); | |
| }); | |
| }); | |
| // 滑块事件 | |
| elements.speedSlider.addEventListener('input', () => { | |
| const speed = parseInt(elements.speedSlider.value); | |
| if (speed < 100) { | |
| elements.speedValue.textContent = '慢'; | |
| } else if (speed < 200) { | |
| elements.speedValue.textContent = '中等'; | |
| } else { | |
| elements.speedValue.textContent = '快'; | |
| } | |
| }); | |
| elements.initialLength.addEventListener('input', () => { | |
| elements.lengthValue.textContent = elements.initialLength.value; | |
| }); | |
| elements.foodCount.addEventListener('input', () => { | |
| elements.foodCountValue.textContent = elements.foodCount.value; | |
| }); | |
| // 全屏按钮 | |
| elements.fullscreenBtn.addEventListener('click', toggleFullscreen); | |
| // 移动设备控制按钮 | |
| elements.upBtn.addEventListener('click', () => { | |
| if (game.direction !== 'down') game.nextDirection = 'up'; | |
| }); | |
| elements.downBtn.addEventListener('click', () => { | |
| if (game.direction !== 'up') game.nextDirection = 'down'; | |
| }); | |
| elements.leftBtn.addEventListener('click', () => { | |
| if (game.direction !== 'right') game.nextDirection = 'left'; | |
| }); | |
| elements.rightBtn.addEventListener('click', () => { | |
| if (game.direction !== 'left') game.nextDirection = 'right'; | |
| }); | |
| } | |
| // 页面加载完成后初始化 | |
| window.addEventListener('load', () => { | |
| initGame(); | |
| setupEventListeners(); | |
| }); | |
| // 切换全屏 | |
| function toggleFullscreen() { | |
| if (!document.fullscreenElement) { | |
| elements.gameContainer.requestFullscreen().catch(err => { | |
| console.error(`全屏错误: ${err.message}`); | |
| }); | |
| elements.fullscreenBtn.innerHTML = '<i class="fas fa-compress"></i>'; | |
| elements.fullscreenBtn.setAttribute('title', '退出全屏'); | |
| } else { | |
| document.exitFullscreen(); | |
| elements.fullscreenBtn.innerHTML = '<i class="fas fa-expand"></i>'; | |
| elements.fullscreenBtn.setAttribute('title', '全屏'); | |
| } | |
| } | |
| // 监听全屏变化 | |
| document.addEventListener('fullscreenchange', () => { | |
| if (!document.fullscreenElement) { | |
| elements.fullscreenBtn.innerHTML = '<i class="fas fa-expand"></i>'; | |
| elements.fullscreenBtn.setAttribute('title', '全屏'); | |
| } | |
| }); | |
| // 响应窗口大小变化 | |
| window.addEventListener('resize', initGame); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=erdes/snake" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |