<!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>
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #f0f0f0;
margin: 0;
font-family: sans-serif;
}
#game-container {
text-align: center;
border: 1px solid #ccc;
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
canvas {
border: 1px solid #000;
background-color: #eee;
display: block;
margin: 0 auto;
}
#score, #timer {
margin-top: 10px;
font-size: 1.2em;
}
#game-over {
margin-top: 20px;
font-size: 1.5em;
color: red;
display: none; /* Initially hidden */
}
.controls {
margin-top: 20px;
}
button {
padding: 10px 20px;
font-size: 1em;
cursor: pointer;
margin: 0 5px;
border: none;
border-radius: 5px;
background-color: #4CAF50;
color: white;
transition: background-color 0.3s ease;
}
button:hover {
background-color: #45a049;
}
</style>
</head>
<body>
<div id="game-container">
<h1>贪吃蛇游戏</h1>
<canvas id="gameCanvas" width="400" height="400"></canvas>
<div id="score">得分: 0</div>
<div id="timer">时间: 0s</div>
<div id="game-over">游戏结束!</div>
<div class="controls">
<button id="startButton">开始游戏</button>
<button id="restartButton" style="display: none;">重新开始</button>
</div>
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreDisplay = document.getElementById('score');
const timerDisplay = document.getElementById('timer');
const gameOverDisplay = document.getElementById('game-over');
const startButton = document.getElementById('startButton');
const restartButton = document.getElementById('restartButton');
const gridSize = 20;
const canvasSize = 400;
let snake = [];
let food = {};
let dx = 0;
let dy = 0;
let score = 0;
let gameRunning = false; // 游戏初始状态为停止
let changingDirection = false;
let startTime = 0;
let elapsedTime = 0;
let gameInterval; // 用于存储游戏循环的定时器ID
// 生成食物
function generateFood() {
food = {
x: Math.floor(Math.random() * (canvasSize / gridSize)) * gridSize,
y: Math.floor(Math.random() * (canvasSize / gridSize)) * gridSize
};
// 确保食物不会生成在蛇身上
snake.forEach(segment => {
if (segment.x === food.x && segment.y === food.y) {
generateFood();
}
});
}
// 绘制游戏元素
function draw() {
// 清空画布
ctx.clearRect(0, 0, canvasSize, canvasSize);
// 绘制食物
ctx.fillStyle = 'red';
ctx.fillRect(food.x, food.y, gridSize, gridSize);
// 绘制蛇
snake.forEach((segment, index) => {
// 蛇头使用深绿色,蛇身使用浅绿色
ctx.fillStyle = index === 0 ? 'darkgreen' : 'lightgreen';
ctx.fillRect(segment.x, segment.y, gridSize, gridSize);
});
}
// 更新游戏状态
function update() {
if (!gameRunning) return;
changingDirection = false; // 重置标志
// 移动蛇
const head = { x: snake[0].x + dx, y: snake[0].y + dy };
snake.unshift(head);
// 检查是否吃到食物
if (head.x === food.x && head.y === food.y) {
score += 10;
scoreDisplay.textContent = '得分: ' + score;
generateFood(); // 生成新的食物
} else {
snake.pop(); // 移除蛇尾
}
// 检查碰撞
checkCollision();
// 更新计时器
elapsedTime = Math.floor((Date.now() - startTime) / 1000);
timerDisplay.textContent = '时间: ' + elapsedTime + 's';
// 重新绘制
draw();
}
// 检查碰撞
function checkCollision() {
const head = snake[0];
// 撞墙
if (head.x < 0 || head.x >= canvasSize || head.y < 0 || head.y >= canvasSize) {
endGame();
return;
}
// 撞到自己
for (let i = 1; i < snake.length; i++) {
if (head.x === snake[i].x && head.y === snake[i].y) {
endGame();
return;
}
}
}
// 游戏结束
function endGame() {
gameRunning = false;
clearInterval(gameInterval); // 停止游戏循环
gameOverDisplay.style.display = 'block';
restartButton.style.display = 'inline-block'; // 显示重新开始按钮
}
// 开始游戏
function startGame() {
// 重置游戏状态
snake = [{ x: 10 * gridSize, y: 10 * gridSize }];
dx = gridSize; // 初始向右移动
dy = 0;
score = 0;
scoreDisplay.textContent = '得分: 0';
gameOverDisplay.style.display = 'none'; // 隐藏游戏结束消息
startButton.style.display = 'none'; // 隐藏开始按钮
restartButton.style.display = 'none'; // 隐藏重新开始按钮
gameRunning = true;
changingDirection = false;
startTime = Date.now(); // 重置计时器开始时间
elapsedTime = 0;
timerDisplay.textContent = '时间: 0s';
generateFood(); // 生成初始食物
draw(); // 绘制初始状态
// 启动游戏主循环
gameInterval = setInterval(update, 100); // 使用 setInterval 控制游戏速度
}
// 绑定按钮事件
startButton.addEventListener('click', startGame);
restartButton.addEventListener('click', startGame); // 重新开始按钮也调用 startGame
// 处理键盘输入
document.addEventListener('keydown', event => {
if (!gameRunning || changingDirection) return; // 游戏未运行时或正在改变方向时忽略按键
changingDirection = true;
const keyPressed = event.key;
const goingUp = dy === -gridSize;
const goingDown = dy === gridSize;
const goingLeft = dx === -gridSize;
const goingRight = dx === gridSize;
console.log(`Key pressed: ${keyPressed}, Current dx: ${dx}, dy: ${dy}`); // 添加日志
// 检查方向键或 WSAD 键
if ((keyPressed === 'ArrowLeft' || keyPressed === 'a') && !goingRight) {
dx = -gridSize;
dy = 0;
} else if ((keyPressed === 'ArrowUp' || keyPressed === 'w') && !goingDown) {
dx = 0;
dy = -gridSize;
} else if ((keyPressed === 'ArrowRight' || keyPressed === 'd') && !goingLeft) {
dx = gridSize;
dy = 0;
} else if ((keyPressed === 'ArrowDown' || keyPressed === 's') && !goingUp) {
dx = 0;
dy = gridSize;
}
console.log(`New dx: ${dx}, New dy: ${dy}`); // 添加日志
});
// 初始绘制空画布和初始食物(在点击开始前)
draw();
generateFood(); // 确保初始食物存在
</script>
</body>
</html>