〇、前言
贪吃蛇是一款经典的休闲游戏,在诺基亚手机时代风靡全球。
作为编程入门者,实现一个贪吃蛇游戏是学习Web前端技术的绝佳练习。
名人说:博观而约取,厚积而薄发。——苏轼《稼说送张琥》
创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊)目录
- 〇、前言
- 效果预览
- 一、HTML结构
- 二、CSS样式设计
- 三、JavaScript游戏逻辑
- 四、代码详解
- 1. 游戏初始化
- 2. 生成食物
- 3. 移动蛇
- 4. 碰撞检测
- 5. 游戏控制
- 五、完整代码
很高兴你打开了这篇博客,更多好用的软件工具,请关注我、订阅专栏《项目探索实验室》,内容持续更新中…
思维速览:
本文将详细讲解如何使用HTML、CSS和JavaScript从零开始创建一个功能完整的贪吃蛇网页游戏(更多功能可以根据个人开发需求拓展)。
我们的贪吃蛇游戏将包含以下功能:
- 基础的游戏逻辑(移动、吃食物、碰撞检测)
- 分数记录和最高分保存
- 游戏控制(开始、暂停、继续)
- 自适应界面设计(支持PC和移动设备)
- 逐步提高的游戏难度
效果预览
▍1️⃣静态展示:
▍2️⃣动态展示:
完成后的游戏效果如下:
- 一个20x20格子的游戏场地
- 绿色的蛇,红色的食物
- 顶部显示当前分数和历史最高分
- 底部有游戏控制按钮和移动设备专用的方向控制键
一、HTML结构
首先,创建基本的HTML结构:
<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>贪吃蛇小游戏</title><!-- CSS将在这里添加 -->
</head>
<body><h1>贪吃蛇小游戏</h1><div class="game-container"><div class="game-header"><div>分数: <span id="score">0</span></div><div>最高分: <span id="high-score">0</span></div></div><div id="game-board"></div><div class="controls"><button id="start-button">开始游戏</button><div class="mobile-controls"><button class="up">↑</button><button class="left">←</button><button class="right">→</button><button class="down">↓</button></div></div></div><!-- JavaScript将在这里添加 -->
</body>
</html>
这个HTML结构包含了:
- 游戏标题
- 分数和最高分显示区域
- 游戏主画布(game-board)
- 游戏控制按钮
- 移动设备的方向控制按钮
二、CSS样式设计
接下来,我们需要添加CSS样式,使游戏看起来更加美观:
<style>body {display: flex;flex-direction: column;align-items: center;justify-content: center;height: 100vh;margin: 0;background-color: #f0f0f0;font-family: Arial, sans-serif;}.game-container {display: flex;flex-direction: column;align-items: center;}.game-header {display: flex;justify-content: space-between;width: 400px;margin-bottom: 10px;}#game-board {width: 400px;height: 400px;border: 2px solid #333;position: relative;background-color: #eee;}.snake-part {width: 20px;height: 20px;background-color: #4CAF50;position: absolute;border-radius: 2px;}.snake-head {background-color: #388E3C;}.food {width: 20px;height: 20px;background-color: #F44336;position: absolute;border-radius: 50%;}.controls {margin-top: 20px;display: flex;flex-direction: column;align-items: center;}button {padding: 10px 20px;margin: 5px;font-size: 16px;cursor: pointer;background-color: #2196F3;color: white;border: none;border-radius: 4px;}button:hover {background-color: #0b7dda;}.mobile-controls {display: grid;grid-template-columns: 1fr 1fr 1fr;grid-template-rows: 1fr 1fr 1fr;gap: 5px;margin-top: 15px;max-width: 200px;}.mobile-controls button {padding: 15px;margin: 0;}.up {grid-column: 2;grid-row: 1;}.left {grid-column: 1;grid-row: 2;}.right {grid-column: 3;grid-row: 2;}.down {grid-column: 2;grid-row: 3;}@media (max-width: 500px) {#game-board {width: 300px;height: 300px;}.game-header {width: 300px;}}
</style>
这些CSS样式:
- 使用Flexbox和Grid布局
- 设计蛇和食物的外观
- 美化按钮和控件
- 添加响应式设计,适应不同屏幕尺寸
三、JavaScript游戏逻辑
最后,也是最重要的部分,我们需要实现游戏的核心逻辑:
<script>document.addEventListener('DOMContentLoaded', () => {// 游戏变量const boardSize = 20; // 20x20 格子const gridSize = 20; // 每个格子的大小(像素)const board = document.getElementById('game-board');const scoreElement = document.getElementById('score');const highScoreElement = document.getElementById('high-score');const startButton = document.getElementById('start-button');let snake = []; // 蛇的身体部分坐标let food = null; // 食物坐标let direction = 'right'; // 初始方向let nextDirection = 'right';let gameInterval = null;let score = 0;let highScore = localStorage.getItem('snakeHighScore') || 0;let gameSpeed = 150; // 游戏速度,毫秒let gameStarted = false;let gamePaused = false;highScoreElement.textContent = highScore;// 初始化游戏function initGame() {clearBoard();// 初始化蛇snake = [{x: 5, y: 10}, // 头部{x: 4, y: 10},{x: 3, y: 10}];// 重置游戏状态direction = 'right';nextDirection = 'right';score = 0;scoreElement.textContent = score;// 生成第一个食物generateFood();// 渲染初始状态renderSnake();renderFood();}// 清空游戏板function clearBoard() {board.innerHTML = '';}// 生成食物function generateFood() {let foodPosition;let onSnake;do {// 随机生成食物位置foodPosition = {x: Math.floor(Math.random() * boardSize),y: Math.floor(Math.random() * boardSize)};// 检查食物是否与蛇重叠onSnake = snake.some(part => part.x === foodPosition.x && part.y === foodPosition.y);} while (onSnake);food = foodPosition;}// 渲染蛇function renderSnake() {snake.forEach((part, index) => {const snakePart = document.createElement('div');snakePart.className = 'snake-part';if (index === 0) {snakePart.classList.add('snake-head');}snakePart.style.left = `${part.x * gridSize}px`;snakePart.style.top = `${part.y * gridSize}px`;board.appendChild(snakePart);});}// 渲染食物function renderFood() {const foodElement = document.createElement('div');foodElement.className = 'food';foodElement.style.left = `${food.x * gridSize}px`;foodElement.style.top = `${food.y * gridSize}px`;board.appendChild(foodElement);}// 移动蛇function moveSnake() {// 更新方向direction = nextDirection;// 获取蛇头的当前位置const head = {...snake[0]};// 根据方向移动蛇头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;}// 将新头部添加到蛇的开始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();// 增加游戏速度if (gameSpeed > 50) {gameSpeed -= 2;clearInterval(gameInterval);gameInterval = setInterval(gameLoop, gameSpeed);}} else {// 没吃到食物,移除蛇尾snake.pop();}// 重新渲染游戏clearBoard();renderSnake();renderFood();}// 检查碰撞function checkCollision(head) {// 检查墙壁碰撞if (head.x < 0 || head.x >= boardSize || head.y < 0 || head.y >= boardSize) {return true;}// 检查自身碰撞return snake.some((part, index) => {// 跳过第一个元素,因为它就是头部if (index === 0) return false;return part.x === head.x && part.y === head.y;});}// 游戏结束function gameOver() {clearInterval(gameInterval);alert(`游戏结束! 你的得分: ${score}`);gameStarted = false;startButton.textContent = '开始游戏';}// 游戏循环function gameLoop() {moveSnake();}// 开始游戏function startGame() {if (gameStarted) {// 如果游戏已经开始,暂停或继续if (gamePaused) {// 继续游戏gameInterval = setInterval(gameLoop, gameSpeed);startButton.textContent = '暂停游戏';gamePaused = false;} else {// 暂停游戏clearInterval(gameInterval);startButton.textContent = '继续游戏';gamePaused = true;}} else {// 开始新游戏initGame();gameInterval = setInterval(gameLoop, gameSpeed);gameStarted = true;gamePaused = false;startButton.textContent = '暂停游戏';}}// 处理键盘输入document.addEventListener('keydown', e => {if (!gameStarted || gamePaused) return;// 防止方向键引起页面滚动if(['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', ' '].includes(e.key)) {e.preventDefault();}// 更新方向switch (e.key) {case 'ArrowUp':if (direction !== 'down') nextDirection = 'up';break;case 'ArrowDown':if (direction !== 'up') nextDirection = 'down';break;case 'ArrowLeft':if (direction !== 'right') nextDirection = 'left';break;case 'ArrowRight':if (direction !== 'left') nextDirection = 'right';break;}});// 移动设备控制按钮document.querySelector('.up').addEventListener('click', () => {if (direction !== 'down' && gameStarted && !gamePaused) nextDirection = 'up';});document.querySelector('.down').addEventListener('click', () => {if (direction !== 'up' && gameStarted && !gamePaused) nextDirection = 'down';});document.querySelector('.left').addEventListener('click', () => {if (direction !== 'right' && gameStarted && !gamePaused) nextDirection = 'left';});document.querySelector('.right').addEventListener('click', () => {if (direction !== 'left' && gameStarted && !gamePaused) nextDirection = 'right';});// 开始游戏按钮startButton.addEventListener('click', startGame);// 初始化游戏initGame();});
</script>
四、代码详解
1. 游戏初始化
function initGame() {clearBoard();// 初始化蛇snake = [{x: 5, y: 10}, // 头部{x: 4, y: 10},{x: 3, y: 10}];// 重置游戏状态direction = 'right';nextDirection = 'right';score = 0;scoreElement.textContent = score;// 生成第一个食物generateFood();// 渲染初始状态renderSnake();renderFood();
}
这个函数负责:
- 清空游戏板
- 创建初始长度为3的蛇
- 重置方向和分数
- 生成食物
- 渲染初始游戏状态
2. 生成食物
function generateFood() {let foodPosition;let onSnake;do {// 随机生成食物位置foodPosition = {x: Math.floor(Math.random() * boardSize),y: Math.floor(Math.random() * boardSize)};// 检查食物是否与蛇重叠onSnake = snake.some(part => part.x === foodPosition.x && part.y === foodPosition.y);} while (onSnake);food = foodPosition;
}
这个函数:
- 随机生成食物的位置
- 确保食物不会出现在蛇身上
- 使用do-while循环直到找到合适的位置
3. 移动蛇
function moveSnake() {// 更新方向direction = nextDirection;// 获取蛇头的当前位置const head = {...snake[0]};// 根据方向移动蛇头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;}// 将新头部添加到蛇的开始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();// 增加游戏速度if (gameSpeed > 50) {gameSpeed -= 2;clearInterval(gameInterval);gameInterval = setInterval(gameLoop, gameSpeed);}} else {// 没吃到食物,移除蛇尾snake.pop();}// 重新渲染游戏clearBoard();renderSnake();renderFood();
}
这个函数是游戏的核心,它:
- 根据当前方向移动蛇头
- 检查是否发生碰撞
- 如果吃到食物,增加分数,生成新食物,加快游戏速度
- 如果没吃到食物,移除蛇尾(保持长度不变)
- 更新游戏界面
4. 碰撞检测
function checkCollision(head) {// 检查墙壁碰撞if (head.x < 0 || head.x >= boardSize || head.y < 0 || head.y >= boardSize) {return true;}// 检查自身碰撞return snake.some((part, index) => {// 跳过第一个元素,因为它就是头部if (index === 0) return false;return part.x === head.x && part.y === head.y;});
}
这个函数检查两种碰撞情况:
- 蛇头撞到墙壁(超出游戏边界)
- 蛇头撞到自己的身体
5. 游戏控制
function startGame() {if (gameStarted) {// 如果游戏已经开始,暂停或继续if (gamePaused) {// 继续游戏gameInterval = setInterval(gameLoop, gameSpeed);startButton.textContent = '暂停游戏';gamePaused = false;} else {// 暂停游戏clearInterval(gameInterval);startButton.textContent = '继续游戏';gamePaused = true;}} else {// 开始新游戏initGame();gameInterval = setInterval(gameLoop, gameSpeed);gameStarted = true;gamePaused = false;startButton.textContent = '暂停游戏';}
}
这个函数处理游戏的控制逻辑:
- 开始新游戏
- 暂停正在进行的游戏
- 继续已暂停的游戏
五、完整代码
最后,我们将上面的HTML、CSS和JavaScript代码合并,得到完整的贪吃蛇游戏:
<!--创作者:Code_流苏(CSDN)-->
<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>贪吃蛇小游戏</title><style>body {display: flex;flex-direction: column;align-items: center;justify-content: center;height: 100vh;margin: 0;background-color: #f0f0f0;font-family: Arial, sans-serif;}.game-container {display: flex;flex-direction: column;align-items: center;}.game-header {display: flex;justify-content: space-between;width: 400px;margin-bottom: 10px;}#game-board {width: 400px;height: 400px;border: 2px solid #333;position: relative;background-color: #eee;}.snake-part {width: 20px;height: 20px;background-color: #4CAF50;position: absolute;border-radius: 2px;}.snake-head {background-color: #388E3C;}.food {width: 20px;height: 20px;background-color: #F44336;position: absolute;border-radius: 50%;}.controls {margin-top: 20px;display: flex;flex-direction: column;align-items: center;}button {padding: 10px 20px;margin: 5px;font-size: 16px;cursor: pointer;background-color: #2196F3;color: white;border: none;border-radius: 4px;}button:hover {background-color: #0b7dda;}.mobile-controls {display: grid;grid-template-columns: 1fr 1fr 1fr;grid-template-rows: 1fr 1fr 1fr;gap: 5px;margin-top: 15px;max-width: 200px;}.mobile-controls button {padding: 15px;margin: 0;}.up {grid-column: 2;grid-row: 1;}.left {grid-column: 1;grid-row: 2;}.right {grid-column: 3;grid-row: 2;}.down {grid-column: 2;grid-row: 3;}@media (max-width: 500px) {#game-board {width: 300px;height: 300px;}.game-header {width: 300px;}}</style>
</head>
<body><h1>贪吃蛇小游戏</h1><div class="game-container"><div class="game-header"><div>分数: <span id="score">0</span></div><div>最高分: <span id="high-score">0</span></div></div><div id="game-board"></div><div class="controls"><button id="start-button">开始游戏</button><div class="mobile-controls"><button class="up">↑</button><button class="left">←</button><button class="right">→</button><button class="down">↓</button></div></div></div><script>document.addEventListener('DOMContentLoaded', () => {// 游戏变量const boardSize = 20; // 20x20 格子const gridSize = 20; // 每个格子的大小(像素)const board = document.getElementById('game-board');const scoreElement = document.getElementById('score');const highScoreElement = document.getElementById('high-score');const startButton = document.getElementById('start-button');let snake = []; // 蛇的身体部分坐标let food = null; // 食物坐标let direction = 'right'; // 初始方向let nextDirection = 'right';let gameInterval = null;let score = 0;let highScore = localStorage.getItem('snakeHighScore') || 0;let gameSpeed = 150; // 游戏速度,毫秒let gameStarted = false;let gamePaused = false;highScoreElement.textContent = highScore;// 初始化游戏function initGame() {clearBoard();// 初始化蛇snake = [{x: 5, y: 10}, // 头部{x: 4, y: 10},{x: 3, y: 10}];// 重置游戏状态direction = 'right';nextDirection = 'right';score = 0;scoreElement.textContent = score;// 生成第一个食物generateFood();// 渲染初始状态renderSnake();renderFood();}// 清空游戏板function clearBoard() {board.innerHTML = '';}// 生成食物function generateFood() {let foodPosition;let onSnake;do {// 随机生成食物位置foodPosition = {x: Math.floor(Math.random() * boardSize),y: Math.floor(Math.random() * boardSize)};// 检查食物是否与蛇重叠onSnake = snake.some(part => part.x === foodPosition.x && part.y === foodPosition.y);} while (onSnake);food = foodPosition;}// 渲染蛇function renderSnake() {snake.forEach((part, index) => {const snakePart = document.createElement('div');snakePart.className = 'snake-part';if (index === 0) {snakePart.classList.add('snake-head');}snakePart.style.left = `${part.x * gridSize}px`;snakePart.style.top = `${part.y * gridSize}px`;board.appendChild(snakePart);});}// 渲染食物function renderFood() {const foodElement = document.createElement('div');foodElement.className = 'food';foodElement.style.left = `${food.x * gridSize}px`;foodElement.style.top = `${food.y * gridSize}px`;board.appendChild(foodElement);}// 移动蛇function moveSnake() {// 更新方向direction = nextDirection;// 获取蛇头的当前位置const head = {...snake[0]};// 根据方向移动蛇头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;}// 将新头部添加到蛇的开始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();// 增加游戏速度if (gameSpeed > 50) {gameSpeed -= 2;clearInterval(gameInterval);gameInterval = setInterval(gameLoop, gameSpeed);}} else {// 没吃到食物,移除蛇尾snake.pop();}// 重新渲染游戏clearBoard();renderSnake();renderFood();}// 检查碰撞function checkCollision(head) {// 检查墙壁碰撞if (head.x < 0 || head.x >= boardSize || head.y < 0 || head.y >= boardSize) {return true;}// 检查自身碰撞return snake.some((part, index) => {// 跳过第一个元素,因为它就是头部if (index === 0) return false;return part.x === head.x && part.y === head.y;});}// 游戏结束function gameOver() {clearInterval(gameInterval);alert(`游戏结束! 你的得分: ${score}`);gameStarted = false;startButton.textContent = '开始游戏';}// 游戏循环function gameLoop() {moveSnake();}// 开始游戏function startGame() {if (gameStarted) {// 如果游戏已经开始,暂停或继续if (gamePaused) {// 继续游戏gameInterval = setInterval(gameLoop, gameSpeed);startButton.textContent = '暂停游戏';gamePaused = false;} else {// 暂停游戏clearInterval(gameInterval);startButton.textContent = '继续游戏';gamePaused = true;}} else {// 开始新游戏initGame();gameInterval = setInterval(gameLoop, gameSpeed);gameStarted = true;gamePaused = false;startButton.textContent = '暂停游戏';}}// 处理键盘输入document.addEventListener('keydown', e => {if (!gameStarted || gamePaused) return;// 防止方向键引起页面滚动if(['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', ' '].includes(e.key)) {e.preventDefault();}// 更新方向switch (e.key) {case 'ArrowUp':if (direction !== 'down') nextDirection = 'up';break;case 'ArrowDown':if (direction !== 'up') nextDirection = 'down';break;case 'ArrowLeft':if (direction !== 'right') nextDirection = 'left';break;case 'ArrowRight':if (direction !== 'left') nextDirection = 'right';break;}});// 移动设备控制按钮document.querySelector('.up').addEventListener('click', () => {if (direction !== 'down' && gameStarted && !gamePaused) nextDirection = 'up';});document.querySelector('.down').addEventListener('click', () => {if (direction !== 'up' && gameStarted && !gamePaused) nextDirection = 'down';});document.querySelector('.left').addEventListener('click', () => {if (direction !== 'right' && gameStarted && !gamePaused) nextDirection = 'left';});document.querySelector('.right').addEventListener('click', () => {if (direction !== 'left' && gameStarted && !gamePaused) nextDirection = 'right';});// 开始游戏按钮startButton.addEventListener('click', startGame);// 初始化游戏initGame();});</script>
</body>
</html>
很感谢你能看到这里,如果你有哪些想学习的项目,欢迎在评论区分享!
创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊)