<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>贪吃蛇游戏</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Arial', sans-serif;
background: linear-gradient(135deg, #1a2a6c, #b21f1f, #1a2a6c);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.game-container {
background-color: rgba(0, 0, 0, 0.8);
border-radius: 20px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
padding: 25px;
text-align: center;
max-width: 500px;
width: 100%;
}
h1 {
color: #4CAF50;
margin-bottom: 15px;
font-size: 2.5rem;
text-shadow: 0 0 10px rgba(76, 175, 80, 0.5);
}
.game-info {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
background: rgba(30, 30, 30, 0.7);
padding: 15px;
border-radius: 10px;
color: white;
font-size: 1.2rem;
}
#score {
color: #FFD700;
font-weight: bold;
}
#high-score {
color: #FF6347;
font-weight: bold;
}
.game-board {
background-color: #0a1f0a;
border: 2px solid #2E7D32;
border-radius: 8px;
display: block;
margin: 0 auto;
box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.5);
}
.controls {
margin-top: 25px;
display: flex;
flex-direction: column;
gap: 15px;
}
.btn {
background: linear-gradient(to bottom, #4CAF50, #2E7D32);
color: white;
border: none;
padding: 12px 20px;
font-size: 1.1rem;
border-radius: 50px;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
.btn:hover {
transform: translateY(-3px);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.4);
background: linear-gradient(to bottom, #66BB6A, #388E3C);
}
.btn:active {
transform: translateY(1px);
}
#restart-btn {
background: linear-gradient(to bottom, #2196F3, #0D47A1);
}
#restart-btn:hover {
background: linear-gradient(to bottom, #42A5F5, #1565C0);
}
.instructions {
margin-top: 25px;
background: rgba(30, 30, 30, 0.7);
padding: 15px;
border-radius: 10px;
color: #ddd;
font-size: 0.9rem;
line-height: 1.5;
}
.instructions h3 {
color: #4CAF50;
margin-bottom: 10px;
}
.game-over {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.9);
padding: 30px;
border-radius: 15px;
text-align: center;
display: none;
z-index: 10;
border: 2px solid #f44336;
box-shadow: 0 0 30px rgba(244, 67, 54, 0.5);
}
.game-over h2 {
color: #f44336;
font-size: 2.5rem;
margin-bottom: 20px;
}
.game-over p {
color: white;
font-size: 1.5rem;
margin-bottom: 25px;
}
.mobile-controls {
display: none;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(2, 1fr);
gap: 10px;
margin-top: 20px;
}
.mobile-btn {
background: rgba(76, 175, 80, 0.3);
color: white;
border: 2px solid #4CAF50;
border-radius: 10px;
padding: 15px;
font-size: 1.5rem;
cursor: pointer;
user-select: none;
}
.up-btn { grid-column: 2; grid-row: 1; }
.left-btn { grid-column: 1; grid-row: 2; }
.right-btn { grid-column: 3; grid-row: 2; }
.down-btn { grid-column: 2; grid-row: 2; }
@media (max-width: 600px) {
.mobile-controls {
display: grid;
}
.game-container {
padding: 15px;
}
h1 {
font-size: 2rem;
}
}
</style>
</head>
<body>
<div class="game-container">
<h1>贪吃蛇游戏</h1>
<div class="game-info">
<div>得分: <span id="score">0</span></div>
<div>最高分: <span id="high-score">0</span></div>
</div>
<canvas id="game-board" class="game-board" width="400" height="400"></canvas>
<div class="mobile-controls">
<div class="mobile-btn up-btn">↑</div>
<div class="mobile-btn left-btn">←</div>
<div class="mobile-btn down-btn">↓</div>
<div class="mobile-btn right-btn">→</div>
</div>
<div class="controls">
<button id="start-btn" class="btn">开始游戏</button>
<button id="pause-btn" class="btn">暂停游戏</button>
<button id="restart-btn" class="btn">重新开始</button>
</div>
<div class="instructions">
<h3>游戏说明</h3>
<p>使用方向键控制蛇的移动,吃到食物后蛇会变长,避免撞到墙壁或自己的身体。</p>
</div>
</div>
<div id="game-over" class="game-over">
<h2>游戏结束!</h2>
<p>最终得分: <span id="final-score">0</span></p>
<button id="play-again-btn" class="btn">再玩一次</button>
</div>
<script>
// 游戏变量
const canvas = document.getElementById('game-board');
const ctx = canvas.getContext('2d');
const scoreDisplay = document.getElementById('score');
const highScoreDisplay = document.getElementById('high-score');
const finalScoreDisplay = document.getElementById('final-score');
const startBtn = document.getElementById('start-btn');
const pauseBtn = document.getElementById('pause-btn');
const restartBtn = document.getElementById('restart-btn');
const playAgainBtn = document.getElementById('play-again-btn');
const gameOverScreen = document.getElementById('game-over');
// 移动端控制按钮
const upBtn = document.querySelector('.up-btn');
const downBtn = document.querySelector('.down-btn');
const leftBtn = document.querySelector('.left-btn');
const rightBtn = document.querySelector('.right-btn');
// 游戏设置
const gridSize = 20;
const tileCount = canvas.width / gridSize;
let speed = 120; // 毫秒
// 游戏状态
let snake = [];
let food = {};
let dx = 0;
let dy = 0;
let score = 0;
let highScore = localStorage.getItem('snakeHighScore') || 0;
let gameRunning = false;
let gamePaused = false;
let gameLoop;
// 初始化游戏
function initGame() {
// 初始化蛇的位置
snake = [
{x: 10, y: 10},
{x: 9, y: 10},
{x: 8, y: 10}
];
// 生成食物
generateFood();
// 重置移动方向
dx = 1;
dy = 0;
// 重置分数
score = 0;
scoreDisplay.textContent = score;
highScoreDisplay.textContent = highScore;
// 隐藏游戏结束界面
gameOverScreen.style.display = 'none';
}
// 生成食物
function generateFood() {
food = {
x: Math.floor(Math.random() * tileCount),
y: Math.floor(Math.random() * tileCount)
};
// 确保食物不会生成在蛇身上
for (let segment of snake) {
if (segment.x === food.x && segment.y === food.y) {
return generateFood();
}
}
}
// 绘制游戏
function draw() {
// 清空画布
ctx.fillStyle = '#0a1f0a';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 绘制网格背景
ctx.strokeStyle = '#0d2b0d';
for (let i = 0; i < tileCount; i++) {
for (let j = 0; j < tileCount; j++) {
if ((i + j) % 2 === 0) {
ctx.fillRect(i * gridSize, j * gridSize, gridSize, gridSize);
}
}
}
// 绘制蛇
snake.forEach((segment, index) => {
if (index === 0) {
// 蛇头
ctx.fillStyle = '#4CAF50';
} else {
// 蛇身
ctx.fillStyle = '#2E7D32';
}
ctx.fillRect(segment.x * gridSize, segment.y * gridSize, gridSize - 1, gridSize - 1);
// 添加蛇身的细节
if (index !== 0) {
ctx.fillStyle = '#1B5E20';
ctx.fillRect(segment.x * gridSize + 4, segment.y * gridSize + 4, gridSize - 8, gridSize - 8);
}
});
// 绘制食物
ctx.fillStyle = '#FF5252';
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 = '#FF8A80';
ctx.beginPath();
ctx.arc(
food.x * gridSize + gridSize/3,
food.y * gridSize + gridSize/3,
gridSize/6,
0,
Math.PI * 2
);
ctx.fill();
}
// 更新游戏状态
function update() {
// 计算新头部位置
const head = {x: snake[0].x + dx, y: snake[0].y + dy};
// 检查碰撞边界
if (head.x < 0 || head.x >= tileCount || head.y < 0 || head.y >= tileCount) {
gameOver();
return;
}
// 检查碰撞自身
for (let i = 0; i < snake.length; i++) {
if (snake[i].x === head.x && snake[i].y === head.y) {
gameOver();
return;
}
}
// 将新头部添加到蛇的前面
snake.unshift(head);
// 检查是否吃到食物
if (head.x === food.x && head.y === food.y) {
// 增加分数
score += 10;
scoreDisplay.textContent = score;
// 生成新的食物
generateFood();
// 提高速度(但不超过最大速度)
if (speed > 60) {
speed -= 2;
}
} else {
// 如果没有吃到食物,移除尾部
snake.pop();
}
}
// 游戏主循环
function gameStep() {
if (!gamePaused) {
update();
draw();
}
}
// 开始游戏
function startGame() {
if (!gameRunning) {
initGame();
gameRunning = true;
gamePaused = false;
startBtn.textContent = "继续游戏";
gameLoop = setInterval(gameStep, speed);
} else if (gamePaused) {
gamePaused = false;
startBtn.textContent = "继续游戏";
}
}
// 暂停游戏
function pauseGame() {
if (gameRunning && !gamePaused) {
gamePaused = true;
startBtn.textContent = "继续游戏";
}
}
// 重新开始游戏
function restartGame() {
clearInterval(gameLoop);
gameRunning = false;
gamePaused = false;
startBtn.textContent = "开始游戏";
initGame();
draw();
}
// 游戏结束
function gameOver() {
clearInterval(gameLoop);
gameRunning = false;
gamePaused = false;
startBtn.textContent = "开始游戏";
// 更新最高分
if (score > highScore) {
highScore = score;
localStorage.setItem('snakeHighScore', highScore);
highScoreDisplay.textContent = highScore;
}
// 显示游戏结束界面
finalScoreDisplay.textContent = score;
gameOverScreen.style.display = 'block';
}
// 键盘控制
function handleKeydown(e) {
// 防止反向移动
switch(e.key) {
case 'ArrowUp':
if (dy === 0) {
dx = 0;
dy = -1;
}
break;
case 'ArrowDown':
if (dy === 0) {
dx = 0;
dy = 1;
}
break;
case 'ArrowLeft':
if (dx === 0) {
dx = -1;
dy = 0;
}
break;
case 'ArrowRight':
if (dx === 0) {
dx = 1;
dy = 0;
}
break;
}
}
// 移动端控制
function setupMobileControls() {
upBtn.addEventListener('click', () => {
if (dy === 0) {
dx = 0;
dy = -1;
}
});
downBtn.addEventListener('click', () => {
if (dy === 0) {
dx = 0;
dy = 1;
}
});
leftBtn.addEventListener('click', () => {
if (dx === 0) {
dx = -1;
dy = 0;
}
});
rightBtn.addEventListener('click', () => {
if (dx === 0) {
dx = 1;
dy = 0;
}
});
}
// 事件监听器
startBtn.addEventListener('click', startGame);
pauseBtn.addEventListener('click', pauseGame);
restartBtn.addEventListener('click', restartGame);
playAgainBtn.addEventListener('click', restartGame);
document.addEventListener('keydown', handleKeydown);
// 初始化游戏
window.onload = function() {
initGame();
draw();
setupMobileControls();
};
</script>
</body>
</html>
index.html
index.html