保存代码为html文件,双击打开即可
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>现代贪吃蛇 | Snake Game</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#4ADE80', // 蛇的颜色-绿色
secondary: '#EF4444', // 食物颜色-红色
dark: '#1E293B',
light: '#F8FAFC'
},
fontFamily: {
game: ['"Press Start 2P"', 'cursive', 'sans-serif'],
sans: ['Inter', 'system-ui', 'sans-serif']
},
}
}
}
</script>
<style type="text/tailwindcss">
@layer utilities {
.game-shadow {
box-shadow: 0 0 20px rgba(74, 222, 128, 0.5);
}
.btn-hover {
@apply transition-all duration-200 hover:scale-105 active:scale-95;
}
.control-btn {
@apply w-14 h-14 md:w-16 md:h-16 rounded-lg bg-dark flex items-center justify-center text-light text-xl md:text-2xl btn-hover;
}
}
/* 引入游戏风格字体 */
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
</style>
</head>
<body class="bg-gradient-to-br from-dark to-slate-800 min-h-screen text-light flex flex-col items-center justify-center p-4 font-sans">
<div class="max-w-4xl w-full mx-auto">
<!– 游戏标题和分数 –>
<header class="flex flex-col md:flex-row justify-between items-center mb-6 gap-4">
<h1 class="text-[clamp(1.5rem,3vw,2.5rem)] font-game text-primary text-center">贪吃蛇</h1>
<div class="flex gap-4 w-full md:w-auto justify-center">
<div class="bg-slate-700/50 backdrop-blur-sm px-4 py-2 rounded-lg flex items-center">
<i class="fa fa-star text-yellow-400 mr-2"></i>
<span class="font-bold">分数:</span>
<span id="score" class="ml-2 text-primary">0</span>
</div>
<div class="bg-slate-700/50 backdrop-blur-sm px-4 py-2 rounded-lg flex items-center">
<i class="fa fa-trophy text-yellow-500 mr-2"></i>
<span class="font-bold">最高分:</span>
<span id="highScore" class="ml-2 text-primary">0</span>
</div>
</div>
</header>
<!– 游戏容器 –>
<div class="relative mx-auto">
<!– 游戏画布 –>
<canvas id="gameCanvas" class="bg-slate-900 rounded-lg game-shadow"></canvas>
<!– 开始游戏遮罩 –>
<div id="startScreen" class="absolute inset-0 bg-dark/80 backdrop-blur-sm rounded-lg flex flex-col items-center justify-center gap-6 z-10">
<h2 class="text-2xl md:text-3xl font-game text-primary text-center">开始游戏</h2>
<p class="text-center px-6 max-w-md">使用方向键或屏幕按钮控制蛇移动,吃到食物得分,撞到墙壁或自己则游戏结束</p>
<button id="startBtn" class="bg-primary text-dark font-bold px-6 py-3 rounded-lg btn-hover">
<i class="fa fa-play mr-2"></i>开始
</button>
</div>
<!– 游戏结束遮罩 –>
<div id="gameOverScreen" class="absolute inset-0 bg-dark/80 backdrop-blur-sm rounded-lg flex flex-col items-center justify-center gap-6 z-10 hidden">
<h2 class="text-2xl md:text-3xl font-game text-secondary text-center">游戏结束</h2>
<p class="text-xl">最终得分: <span id="finalScore" class="text-primary font-bold">0</span></p>
<button id="restartBtn" class="bg-primary text-dark font-bold px-6 py-3 rounded-lg btn-hover">
<i class="fa fa-refresh mr-2"></i>再来一局
</button>
</div>
</div>
<!– 游戏控制区 –>
<div class="mt-8 flex flex-col gap-6">
<!– 难度选择 –>
<div class="flex justify-center gap-4">
<span class="text-center self-center">难度:</span>
<button class="difficulty-btn bg-slate-700 hover:bg-slate-600 px-4 py-2 rounded-lg btn-hover" data-speed="150">简单</button>
<button class="difficulty-btn bg-primary text-dark font-bold px-4 py-2 rounded-lg btn-hover" data-speed="100">中等</button>
<button class="difficulty-btn bg-slate-700 hover:bg-slate-600 px-4 py-2 rounded-lg btn-hover" data-speed="60">困难</button>
</div>
<!– 移动控制按钮 (主要用于移动设备) –>
<div class="md:hidden flex flex-col items-center gap-2">
<div class="control-btn" data-direction="up">
<i class="fa fa-chevron-up"></i>
</div>
<div class="flex gap-2">
<div class="control-btn" data-direction="left">
<i class="fa fa-chevron-left"></i>
</div>
<div class="control-btn" data-direction="down">
<i class="fa fa-chevron-down"></i>
</div>
<div class="control-btn" data-direction="right">
<i class="fa fa-chevron-right"></i>
</div>
</div>
</div>
<!– 游戏说明 –>
<div class="text-center text-sm text-slate-400">
<p>电脑端使用方向键控制,移动端使用屏幕按钮控制</p>
</div>
</div>
</div>
<script>
// 游戏变量
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreElement = document.getElementById('score');
const highScoreElement = document.getElementById('highScore');
const startScreen = document.getElementById('startScreen');
const gameOverScreen = document.getElementById('gameOverScreen');
const finalScoreElement = document.getElementById('finalScore');
const startBtn = document.getElementById('startBtn');
const restartBtn = document.getElementById('restartBtn');
const difficultyBtns = document.querySelectorAll('.difficulty-btn');
// 设置画布尺寸
function setCanvasSize() {
// 确保画布在不同设备上有合适的大小
const maxWidth = window.innerWidth > 768 ? 600 : window.innerWidth – 40;
const size = Math.min(maxWidth, 600);
canvas.width = size;
canvas.height = size;
}
// 初始化画布尺寸
setCanvasSize();
window.addEventListener('resize', setCanvasSize);
// 游戏参数
let snake = [];
let food = {};
let direction = '';
let nextDirection = '';
let score = 0;
let highScore = localStorage.getItem('snakeHighScore') || 0;
let gameSpeed = 100; // 毫秒
let gameInterval;
let isGameRunning = false;
const gridSize = 20; // 网格大小
// 更新高分显示
highScoreElement.textContent = highScore;
// 初始化蛇
function initSnake() {
const center = Math.floor(canvas.width / gridSize / 2) * gridSize;
snake = [
{ x: center, y: center },
{ x: center – gridSize, y: center },
{ x: center – gridSize * 2, y: center }
];
direction = 'right';
nextDirection = 'right';
}
// 随机生成食物位置
function generateFood() {
// 确保食物不会出现在蛇身上
let newFood;
do {
newFood = {
x: Math.floor(Math.random() * (canvas.width / gridSize)) * gridSize,
y: Math.floor(Math.random() * (canvas.height / gridSize)) * gridSize
};
} while (snake.some(segment => segment.x === newFood.x && segment.y === newFood.y));
food = newFood;
}
// 绘制蛇
function drawSnake() {
snake.forEach((segment, index) => {
// 蛇头使用不同的颜色
if (index === 0) {
ctx.fillStyle = '#22c55e'; // 深绿色
} else {
// 蛇身逐渐变色
const greenValue = 174 – index * 5;
ctx.fillStyle = `rgb(74, ${Math.max(greenValue, 100)}, 128)`;
}
ctx.fillRect(segment.x, segment.y, gridSize – 1, gridSize – 1);
// 蛇头绘制眼睛
if (index === 0) {
ctx.fillStyle = '#fff';
const eyeSize = gridSize / 5;
switch (direction) {
case 'right':
ctx.fillRect(segment.x + gridSize – eyeSize * 2, segment.y + eyeSize, eyeSize, eyeSize);
ctx.fillRect(segment.x + gridSize – eyeSize * 2, segment.y + gridSize – eyeSize * 2, eyeSize, eyeSize);
break;
case 'left':
ctx.fillRect(segment.x + eyeSize, segment.y + eyeSize, eyeSize, eyeSize);
ctx.fillRect(segment.x + eyeSize, segment.y + gridSize – eyeSize * 2, eyeSize, eyeSize);
break;
case 'up':
ctx.fillRect(segment.x + eyeSize, segment.y + eyeSize, eyeSize, eyeSize);
ctx.fillRect(segment.x + gridSize – eyeSize * 2, segment.y + eyeSize, eyeSize, eyeSize);
break;
case 'down':
ctx.fillRect(segment.x + eyeSize, segment.y + gridSize – eyeSize * 2, eyeSize, eyeSize);
ctx.fillRect(segment.x + gridSize – eyeSize * 2, segment.y + gridSize – eyeSize * 2, eyeSize, eyeSize);
break;
}
}
});
}
// 绘制食物
function drawFood() {
ctx.fillStyle = '#EF4444'; // 红色食物
// 绘制苹果形状
ctx.beginPath();
ctx.arc(
food.x + gridSize / 2,
food.y + gridSize / 2,
gridSize / 2 – 1,
0,
Math.PI * 2
);
ctx.fill();
// 绘制苹果柄
ctx.fillStyle = '#8B4513';
ctx.beginPath();
ctx.moveTo(food.x + gridSize / 2, food.y);
ctx.lineTo(food.x + gridSize / 2 + 5, food.y – 5);
ctx.lineTo(food.x + gridSize / 2 + 3, food.y – 5);
ctx.lineTo(food.x + gridSize / 2, food.y – 1);
ctx.fill();
}
// 移动蛇
function moveSnake() {
// 更新方向
direction = nextDirection;
// 创建新的头部
const head = { …snake[0] };
// 根据方向移动头部
switch (direction) {
case 'up':
head.y -= gridSize;
break;
case 'down':
head.y += gridSize;
break;
case 'left':
head.x -= gridSize;
break;
case 'right':
head.x += gridSize;
break;
}
// 检查碰撞
if (
head.x < 0 ||
head.x >= canvas.width ||
head.y < 0 ||
head.y >= canvas.height ||
snake.some(segment => segment.x === head.x && segment.y === head.y)
) {
gameOver();
return;
}
// 将新头部添加到蛇
snake.unshift(head);
// 检查是否吃到食物
if (head.x === food.x && head.y === food.y) {
// 加分
score += 10;
scoreElement.textContent = score;
// 更新高分
if (score > highScore) {
highScore = score;
highScoreElement.textContent = highScore;
localStorage.setItem('snakeHighScore', highScore);
}
// 生成新食物
generateFood();
// 播放吃食物音效
playSound('eat');
} else {
// 移除尾部
snake.pop();
}
}
// 绘制游戏
function drawGame() {
// 清空画布
ctx.fillStyle = '#0F172A'; // 深色背景
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 绘制网格(轻微的网格线)
ctx.strokeStyle = 'rgba(148, 163, 184, 0.1)';
ctx.lineWidth = 1;
// 绘制水平线
for (let y = 0; y < canvas.height; y += gridSize) {
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
ctx.stroke();
}
// 绘制垂直线
for (let x = 0; x < canvas.width; x += gridSize) {
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas.height);
ctx.stroke();
}
// 绘制蛇和食物
drawSnake();
drawFood();
}
// 游戏循环
function gameLoop() {
moveSnake();
drawGame();
}
// 开始游戏
function startGame() {
if (isGameRunning) return;
// 重置游戏状态
score = 0;
scoreElement.textContent = score;
startScreen.classList.add('hidden');
gameOverScreen.classList.add('hidden');
// 初始化游戏
initSnake();
generateFood();
// 开始游戏循环
isGameRunning = true;
clearInterval(gameInterval);
gameInterval = setInterval(gameLoop, gameSpeed);
// 播放开始音效
playSound('start');
}
// 游戏结束
function gameOver() {
isGameRunning = false;
clearInterval(gameInterval);
// 显示游戏结束画面
finalScoreElement.textContent = score;
gameOverScreen.classList.remove('hidden');
// 播放游戏结束音效
playSound('gameOver');
}
// 处理方向控制
function handleDirection(newDirection) {
// 防止180度转向
if (
(newDirection === 'up' && direction === 'down') ||
(newDirection === 'down' && direction === 'up') ||
(newDirection === 'left' && direction === 'right') ||
(newDirection === 'right' && direction === 'left')
) {
return;
}
nextDirection = newDirection;
}
// 播放简单音效(使用Web Audio API)
function playSound(type) {
try {
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
// 根据不同类型设置不同的音效
switch (type) {
case 'start':
oscillator.type = 'sine';
oscillator.frequency.setValueAtTime(330, audioContext.currentTime);
oscillator.frequency.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.5);
gainNode.gain.setValueAtTime(0.1, audioContext.currentTime);
oscillator.start();
oscillator.stop(audioContext.currentTime + 0.5);
break;
case 'eat':
oscillator.type = 'sine';
oscillator.frequency.setValueAtTime(523.25, audioContext.currentTime);
oscillator.frequency.exponentialRampToValueAtTime(783.99, audioContext.currentTime + 0.1);
gainNode.gain.setValueAtTime(0.1, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.1);
oscillator.start();
oscillator.stop(audioContext.currentTime + 0.1);
break;
case 'gameOver':
oscillator.type = 'sine';
oscillator.frequency.setValueAtTime(220, audioContext.currentTime);
oscillator.frequency.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.8);
gainNode.gain.setValueAtTime(0.1, audioContext.currentTime);
oscillator.start();
oscillator.stop(audioContext.currentTime + 0.8);
break;
}
} catch (e) {
// 浏览器不支持Web Audio API时不做处理
console.log('音效播放失败:', e);
}
}
// 事件监听
startBtn.addEventListener('click', startGame);
restartBtn.addEventListener('click', startGame);
// 键盘控制
document.addEventListener('keydown', (e) => {
switch (e.key) {
case 'ArrowUp':
handleDirection('up');
break;
case 'ArrowDown':
handleDirection('down');
break;
case 'ArrowLeft':
handleDirection('left');
break;
case 'ArrowRight':
handleDirection('right');
break;
case ' ': // 空格键开始/暂停游戏
if (isGameRunning) {
clearInterval(gameInterval);
isGameRunning = false;
} else if (!startScreen.classList.contains('hidden') || !gameOverScreen.classList.contains('hidden')) {
startGame();
}
break;
}
});
// 屏幕控制按钮
document.querySelectorAll('.control-btn').forEach(btn => {
btn.addEventListener('click', () => {
handleDirection(btn.dataset.direction);
});
});
// 难度选择
difficultyBtns.forEach(btn => {
btn.addEventListener('click', () => {
// 更新UI
difficultyBtns.forEach(b => {
b.classList.remove('bg-primary', 'text-dark', 'font-bold');
b.classList.add('bg-slate-700', 'hover:bg-slate-600');
});
btn.classList.remove('bg-slate-700', 'hover:bg-slate-600');
btn.classList.add('bg-primary', 'text-dark', 'font-bold');
// 更新游戏速度
gameSpeed = parseInt(btn.dataset.speed);
// 如果游戏正在运行,更新间隔
if (isGameRunning) {
clearInterval(gameInterval);
gameInterval = setInterval(gameLoop, gameSpeed);
}
});
});
// 触摸滑动控制(移动设备)
let touchStartX = 0;
let touchStartY = 0;
canvas.addEventListener('touchstart', (e) => {
touchStartX = e.touches[0].clientX;
touchStartY = e.touches[0].clientY;
e.preventDefault(); // 防止触摸事件影响页面滚动
}, { passive: false });
canvas.addEventListener('touchmove', (e) => {
e.preventDefault(); // 防止触摸事件影响页面滚动
}, { passive: false });
canvas.addEventListener('touchend', (e) => {
if (!isGameRunning) return;
const touchEndX = e.changedTouches[0].clientX;
const touchEndY = e.changedTouches[0].clientY;
const diffX = touchEndX – touchStartX;
const diffY = touchEndY – touchStartY;
// 确定滑动方向(水平还是垂直)
if (Math.abs(diffX) > Math.abs(diffY)) {
// 水平滑动
handleDirection(diffX > 0 ? 'right' : 'left');
} else {
// 垂直滑动
handleDirection(diffY > 0 ? 'down' : 'up');
}
e.preventDefault();
}, { passive: false });
// 初始绘制开始界面的背景
drawGame();
</script>
</body>
</html>
评论前必须登录!
注册