贪吃蛇游戏
Remember the snake game that kept us hooked, back in the old days? Here’s a simple JavaScript tutorial to help you build it! Take a sneak peak into the live demo of the game, before you begin!
还记得过去让我们着迷的蛇游戏吗? 这是一个简单JavaScript教程,可以帮助您构建它! 在开始之前,先潜入游戏的现场演示!
This article is meant for those who are new to JavaScript. I’ve broken down the process of creating a snake — game into simple concise steps, which you can follow.
本文适用于JavaScript新手。 我将创建蛇的过程分解为简单的简洁步骤,您可以遵循这些步骤。
The explanation of the code is detailed in the comments in the code.
该代码的解释在代码的注释中有详细说明。
And wait! Already familiar with HTML and CSS? Skip to step 4 for the JavaScript code!
等一下已经熟悉HTML和CSS? 跳到第4步以获取JavaScript代码!
步骤1:创建基本文件 (Step 1: Base file creation)
Beginning the process, initially we would need to create the base files for the project. An HTML file as the foundation, a CSS file for the styling, and the JavaScript file with the code for the game!
开始该过程时,首先我们需要为项目创建基础文件。 一个HTML文件作为基础,一个CSS文件用于样式,而JavaScript文件包含游戏代码!
1. index.html
1. index.html
2. snake_game.css
2. snake_game.css
3. snake_game.js
3. snake_game.js
步骤2:HTML档案 (Step 2: The HTML file)
With HTML, alone you can create the simplest of websites. HTML is the markup language for web pages. To get a thorough idea of the different HTML elements, HTML tags and so on, check out: W3Schools for HTML.
仅使用HTML,您就可以创建最简单的网站。 HTML是网页的标记语言。 要全面了解不同HTML元素,HTML标记等,请查看: W3Schools for HTML 。
Here, the different div elements we have chosen are:
在这里,我们选择的不同div元素是:
1. “gameContainer”, as the base for the game board.
1. “ gameContainer” ,作为游戏板的基础。
2. “scoreContainer”, to keep the scores.
2. “ scoreContainer” ,保持得分。
3. “onScreenControllers”, with the navigation buttons for the mobile screen.
3. “ onScreenControllers” ,带有移动屏幕的导航按钮。
We’ve also linked our style-sheet — ‘snake_game.css’ and loaded our game logic through ‘snake_game.js’ in the html file.
我们还链接了样式表-'snake_game.css',并通过html文件中的'snake_game.js'加载了游戏逻辑。
Check out the comments in the code for a line-by-line explanation.
请查看代码中的注释以获取逐行说明。
代码(HTML) (Code (HTML))
Add the following content to index.html:
将以下内容添加到index.html:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Snake v8</title>
<!-- Link our styles from snake_game.css -->
<link rel="stylesheet" type="text/css" href="snake_game.css" />
</head>
<body>
<!-- #gameContainer is the main game board-->
<div id="gameContainer"></div>
<!-- #scoreContainer contains the scores -->
<div id="scoreContainer">
<div class="scoreBoard">Food: <span id="pointsEarned">0</span></div>
<div class="scoreBoard">Blocks: <span id="blocksTravelled">0</span></div>
</div>
<!-- #onScreenControllers contains the navigation buttons for mobile screens -->
<div id="onScreenControllers">
<button id="leftButton">◀️</button>
<div>
<button id="upButton">🔼</button>
<button id="downButton">🔽</button>
</div>
<button id="rightButton">▶️</button>
</div>
</body>
<!-- #Load our snake_game.js containing the game logic -->
<script src="snake_game.js"></script>
</html>
步骤3:CSS文件(Step 3: The CSS file)
CSS (Cascading Style Sheets) is used to decorate the otherwise plain HTML file. Without using CSS, the amount of designing we can do to the web page is extremely limited. To get a detailed idea about the different CSS tags, check out W3Schools for CSS.
CSS(层叠样式表)用于装饰原本纯净HTML文件。 在不使用CSS的情况下,我们可以对网页进行的设计量非常有限。 要获得有关不同CSS标签的详细信息,请查看W3Schools for CSS 。
Using snake_game.css we have styled every small and big element on our html file, from the game board, to the snake, it’s food, and the onscreen control buttons as well!
使用snake_game.css,我们在html文件中设置了每个大小元素,包括游戏板,蛇,食物以及屏幕控制按钮的样式!
Check out the comments in the code for a line-by-line explanation.
请查看代码中的注释以获取逐行说明。
代码(CSS) (Code (CSS))
Add the following content to snake_game.css:(Play around to create different themes 😉)
将以下内容添加到snake_game.css :(播放以创建不同的主题😉)
body {
background-color: darkslategrey;
text-align: center;
}
/* GAME BOARD STYLES */
#gameContainer {
/*
width and height of .gameBoardPixel should 1/40 of the width and height of #gameContainer,
because it is used in calculation in the jscript.js file
*/
width: 40vw;
height: 40vw;
margin: 2vw auto;
background-color: #0c1021;
border: solid 10px slategrey;
border-radius: 10px;
-webkit-box-shadow: 0px 0px 20px 3px rgba(0, 0, 0, 0.6);
-moz-box-shadow: 0px 0px 20px 3px rgba(0, 0, 0, 0.6);
box-shadow: 0px 0px 20px 3px rgba(0, 0, 0, 0.6);
}
.gameBoardPixel {
/* background-color: slategrey; */
/*
width and height of .gameBoardPixel should 1/40 of the width and height of #gameContainer,
because it is used in calculation in the jscript.js file
*/
width: 1vw;
height: 1vw;
border-radius: 10px;
float: left;
}
/* GAME BOARD STYLES END*/
/* SNAKE STYLES */
.snakeBodyPixel {
background-color: #fd6401;
box-shadow: 0 0 5px #fd6401;
}
/* SNAKE STYLES END*/
/* FOOD STYLES */
.food {
background-color: #68e768;
}
/* FOOD STYLES END*/
/* SCORE STYLES */
#scoreContainer {
width: 40vw;
display: flex;
margin: auto;
justify-content: space-around;
}
.scoreBoard {
border-radius: 10px;
border: solid 5px slategrey;
color: dimgray;
background-color: #0c1021;
display: inline-block;
padding: 1vw;
width: 40%;
-webkit-box-shadow: 0px 0px 20px 3px rgba(0, 0, 0, 0.6);
-moz-box-shadow: 0px 0px 20px 3px rgba(0, 0, 0, 0.6);
box-shadow: 0px 0px 20px 3px rgba(0, 0, 0, 0.6);
}
/* SCORE STYLES END */
/* Hide #onScreenControllers on desktop */
#onScreenControllers {
display: none;
}
@media only screen and (max-width: 768px) {
/* MOBILE */
#gameContainer {
width: 80vw;
height: 80vw;
}
.gameBoardPixel {
width: 2vw;
height: 2vw;
}
#scoreContainer {
width: 80vw;
}
#onScreenControllers {
width: 80vw;
margin: 5vw auto;
display: flex;
justify-content: space-evenly;
align-items: center;
}
#onScreenControllers > div {
display: flex;
flex-direction: column;
}
#onScreenControllers button {
background-color: transparent;
height: 20vw;
width: 20vw;
font-size: 10vw;
border: none;
}
#onScreenControllers button:focus {
outline: none;
}
#onScreenControllers button:active {
background-color: slategray;
}
}
步骤4:游戏逻辑-Javascript文件(Step 4: Game logic — Javascript file)
This is the most important step, or where the game logic comes to play. Here we write code for the javascript file ‘snake_game.js’.
这是最重要的步骤,也就是发挥游戏逻辑的地方。 在这里,我们为javascript文件“ snake_game.js”编写代码。
Initialize the score variables:
初始化分数变量:
let totalFoodAte = 0;
let totalDistanceTravelled = 0;
2. Code for the Game Board pixels:
2.游戏板像素代码:
const gameContainer = document.getElementById("gameContainer");
const createGameBoardPixels = () => {
// Populate the [#gameContainer] div with small div's representing game pixels
for (let i = 1; i <= 1600; ++i) {
gameContainer.innerHTML = `${gameContainer.innerHTML} <div class="gameBoardPixel" id="pixel${i}"></div>`;
}
};
// This variable always holds the updated array of game pixels created by createGameBoardPixels() :
const gameBoardPixels = document.getElementsByClassName("gameBoardPixel");
3. Code for the Food:
3.食品代码:
let currentFoodPostion = 0; // Initially set to 0
const createFood = () => {
// Remove previous food;
gameBoardPixels[currentFoodPostion].classList.remove("food");
// Create new food
currentFoodPostion = Math.random();
currentFoodPostion = Math.floor(currentFoodPostion * 1600);
gameBoardPixels[currentFoodPostion].classList.add("food");
};
4.Code for the Snake 🐍:
4.蛇ake代码:
4.1. Code related to the Direction of the snake:
4.1。 与蛇的方向有关的代码:
// Direction codes (Keyboard key codes for arrow keys):
const LEFT_DIR = 37;
const UP_DIR = 38;
const RIGHT_DIR = 39;
const DOWN_DIR = 40;
// Set snake direction initially to right
let snakeCurrentDirection = RIGHT_DIR;
const changeDirection = newDirectionCode => {
// Change the direction of the snake
if (newDirectionCode == snakeCurrentDirection) return;
if (newDirectionCode == LEFT_DIR && snakeCurrentDirection != RIGHT_DIR) {
snakeCurrentDirection = newDirectionCode;
} else if (newDirectionCode == UP_DIR && snakeCurrentDirection != DOWN_DIR) {
snakeCurrentDirection = newDirectionCode;
} else if (newDirectionCode == RIGHT_DIR && snakeCurrentDirection != LEFT_DIR) {
snakeCurrentDirection = newDirectionCode;
} else if (newDirectionCode == DOWN_DIR && snakeCurrentDirection != UP_DIR) {
snakeCurrentDirection = newDirectionCode;
}
};
4.2. Code related to the Movement of the snake:
4.2。 与蛇的运动有关的代码:
// Let the starting position of the snake be at the middle of game board
let currentSnakeHeadPosition = 799;
let snakeLength = 1000; // Initial length of the snake = 1000
// Move snake continously by calling this function repeatedly :
const moveSnake = () => {
switch (snakeCurrentDirection) {
case LEFT_DIR:
--currentSnakeHeadPosition;
const isSnakeHeadAtLastGameBoardPixelTowardsLeft = currentSnakeHeadPosition % 40 == 39 || currentSnakeHeadPosition < 0;
if (isSnakeHeadAtLastGameBoardPixelTowardsLeft) {
currentSnakeHeadPosition = currentSnakeHeadPosition + 40;
}
break;
case UP_DIR:
currentSnakeHeadPosition = currentSnakeHeadPosition - 40;
const isSnakeHeadAtLastGameBoardPixelTowardsUp = currentSnakeHeadPosition < 0;
if (isSnakeHeadAtLastGameBoardPixelTowardsUp) {
currentSnakeHeadPosition = currentSnakeHeadPosition + 1600;
}
break;
case RIGHT_DIR:
++currentSnakeHeadPosition;
const isSnakeHeadAtLastGameBoardPixelTowardsRight = currentSnakeHeadPosition % 40 == 0;
if (isSnakeHeadAtLastGameBoardPixelTowardsRight) {
currentSnakeHeadPosition = currentSnakeHeadPosition - 40;
}
break;
case DOWN_DIR:
currentSnakeHeadPosition = currentSnakeHeadPosition + 40;
const isSnakeHeadAtLastGameBoardPixelTowardsDown = currentSnakeHeadPosition > 1599;
if (isSnakeHeadAtLastGameBoardPixelTowardsDown) {
currentSnakeHeadPosition = currentSnakeHeadPosition - 1600;
}
break;
default:
break;
}
let nextSnakeHeadPixel = gameBoardPixels[currentSnakeHeadPosition];
// Kill snake if it bites itself:
if (nextSnakeHeadPixel.classList.contains("snakeBodyPixel")) {
// Stop moving the snake
clearInterval(moveSnakeInterval);
if (!alert(`You have ate ${totalFoodAte} food by travelling ${totalDistanceTravelled} blocks.`))
window.location.reload();
}
// If not killed add the snake body:
nextSnakeHeadPixel.classList.add("snakeBodyPixel");
// This fuction removes the snake body from the end of the snake as it moves.
// Also note that snakeLength is used as the timeout interval
setTimeout(() => {
nextSnakeHeadPixel.classList.remove("snakeBodyPixel");
}, snakeLength);
// Update total distance travelled
totalDistanceTravelled++;
// Update in UI:
document.getElementById("blocksTravelled").innerHTML = totalDistanceTravelled;
// If snike bites the food:
if (currentSnakeHeadPosition == currentFoodPostion) {
// Update total food ate
totalFoodAte++;
// Update in UI:
document.getElementById("pointsEarned").innerHTML = totalFoodAte;
// Increase Snake length:
snakeLength = snakeLength + 100;
// Create new food:
createFood();
}
};
5. Code to start the game using the above logic:
5.使用上述逻辑启动游戏代码:
// Create game board pixels:
createGameBoardPixels();
// Create initial food:
createFood();
// Move snake:
// The variable, "moveSnakeInterval" is used to stop the snake on snake killed.
var moveSnakeInterval = setInterval(moveSnake, 80);
// Call change direction function on keyboard key-down event:
addEventListener("keydown", e => changeDirection(e.keyCode));
// CONFIGURE THE ON SCREEN CONTROLLERS:
const leftButton = document.getElementById("leftButton");
const rightButton = document.getElementById("rightButton");
const upButton = document.getElementById("upButton");
const downButton = document.getElementById("downButton");
leftButton.onclick = () => changeDirection(LEFT_DIR);
rightButton.onclick = () => changeDirection(RIGHT_DIR);
upButton.onclick = () => changeDirection(UP_DIR);
downButton.onclick = () => changeDirection(DOWN_DIR);
The complete snake_game.js is here:
完整的snake_game.js在这里:
// GAME_PIXEL_COUNT is the pixels on horizontal or vertical axis of the game board (SQUARE).
const GAME_PIXEL_COUNT = 40;
const SQUARE_OF_GAME_PIXEL_COUNT = Math.pow(GAME_PIXEL_COUNT, 2);
let totalFoodAte = 0;
let totalDistanceTravelled = 0;
/// THE GAME BOARD:
const gameContainer = document.getElementById("gameContainer");
const createGameBoardPixels = () => {
// Populate the [#gameContainer] div with small div's representing game pixels
for (let i = 1; i <= SQUARE_OF_GAME_PIXEL_COUNT; ++i) {
gameContainer.innerHTML = `${gameContainer.innerHTML} <div class="gameBoardPixel" id="pixel${i}"></div>`;
}
};
// This variable always holds the updated array of game pixels created by createGameBoardPixels() :
const gameBoardPixels = document.getElementsByClassName("gameBoardPixel");
/// THE FOOD:
let currentFoodPostion = 0;
const createFood = () => {
// Remove previous food;
gameBoardPixels[currentFoodPostion].classList.remove("food");
// Create new food
currentFoodPostion = Math.random();
currentFoodPostion = Math.floor(
currentFoodPostion * SQUARE_OF_GAME_PIXEL_COUNT
);
gameBoardPixels[currentFoodPostion].classList.add("food");
};
/// THE SNAKE:
// Direction codes (Keyboard key codes for arrow keys):
const LEFT_DIR = 37;
const UP_DIR = 38;
const RIGHT_DIR = 39;
const DOWN_DIR = 40;
// Set snake direction initially to right
let snakeCurrentDirection = RIGHT_DIR;
const changeDirection = newDirectionCode => {
// Change the direction of the snake
if (newDirectionCode == snakeCurrentDirection) return;
if (newDirectionCode == LEFT_DIR && snakeCurrentDirection != RIGHT_DIR) {
snakeCurrentDirection = newDirectionCode;
} else if (newDirectionCode == UP_DIR && snakeCurrentDirection != DOWN_DIR) {
snakeCurrentDirection = newDirectionCode;
} else if (
newDirectionCode == RIGHT_DIR &&
snakeCurrentDirection != LEFT_DIR
) {
snakeCurrentDirection = newDirectionCode;
} else if (newDirectionCode == DOWN_DIR && snakeCurrentDirection != UP_DIR) {
snakeCurrentDirection = newDirectionCode;
}
};
// Let the starting position of the snake be at the middle of game board
let currentSnakeHeadPosition = SQUARE_OF_GAME_PIXEL_COUNT / 2;
// Initial snake length
let snakeLength = 1000;
// Move snake continously by calling this function repeatedly :
const moveSnake = () => {
switch (snakeCurrentDirection) {
case LEFT_DIR:
--currentSnakeHeadPosition;
const isSnakeHeadAtLastGameBoardPixelTowardsLeft =
currentSnakeHeadPosition % GAME_PIXEL_COUNT == GAME_PIXEL_COUNT - 1 ||
currentSnakeHeadPosition < 0;
if (isSnakeHeadAtLastGameBoardPixelTowardsLeft) {
currentSnakeHeadPosition = currentSnakeHeadPosition + GAME_PIXEL_COUNT;
}
break;
case UP_DIR:
currentSnakeHeadPosition = currentSnakeHeadPosition - GAME_PIXEL_COUNT;
const isSnakeHeadAtLastGameBoardPixelTowardsUp =
currentSnakeHeadPosition < 0;
if (isSnakeHeadAtLastGameBoardPixelTowardsUp) {
currentSnakeHeadPosition =
currentSnakeHeadPosition + SQUARE_OF_GAME_PIXEL_COUNT;
}
break;
case RIGHT_DIR:
++currentSnakeHeadPosition;
const isSnakeHeadAtLastGameBoardPixelTowardsRight =
currentSnakeHeadPosition % GAME_PIXEL_COUNT == 0;
if (isSnakeHeadAtLastGameBoardPixelTowardsRight) {
currentSnakeHeadPosition = currentSnakeHeadPosition - GAME_PIXEL_COUNT;
}
break;
case DOWN_DIR:
currentSnakeHeadPosition = currentSnakeHeadPosition + GAME_PIXEL_COUNT;
const isSnakeHeadAtLastGameBoardPixelTowardsDown =
currentSnakeHeadPosition > SQUARE_OF_GAME_PIXEL_COUNT - 1;
if (isSnakeHeadAtLastGameBoardPixelTowardsDown) {
currentSnakeHeadPosition =
currentSnakeHeadPosition - SQUARE_OF_GAME_PIXEL_COUNT;
}
break;
default:
break;
}
let nextSnakeHeadPixel = gameBoardPixels[currentSnakeHeadPosition];
// Kill snake if it bites itself:
if (nextSnakeHeadPixel.classList.contains("snakeBodyPixel")) {
// Stop moving the snake
clearInterval(moveSnakeInterval);
if (
!alert(
`You have ate ${totalFoodAte} food by travelling ${totalDistanceTravelled} blocks.`
)
)
window.location.reload();
}
nextSnakeHeadPixel.classList.add("snakeBodyPixel");
setTimeout(() => {
nextSnakeHeadPixel.classList.remove("snakeBodyPixel");
}, snakeLength);
// Update total distance travelled
totalDistanceTravelled++;
// Update in UI:
document.getElementById("blocksTravelled").innerHTML = totalDistanceTravelled;
if (currentSnakeHeadPosition == currentFoodPostion) {
// Update total food ate
totalFoodAte++;
// Update in UI:
document.getElementById("pointsEarned").innerHTML = totalFoodAte;
// Increase Snake length:
snakeLength = snakeLength + 100;
createFood();
}
};
/// CALL THE FOLLOWING FUNCTIONS TO RUN THE GAME:
// Create game board pixels:
createGameBoardPixels();
// Create initial food:
createFood();
// Move snake:
var moveSnakeInterval = setInterval(moveSnake, 80);
// Call change direction function on keyboard key-down event:
addEventListener("keydown", e => changeDirection(e.keyCode));
// ON SCREEN CONTROLLERS:
const leftButton = document.getElementById("leftButton");
const rightButton = document.getElementById("rightButton");
const upButton = document.getElementById("upButton");
const downButton = document.getElementById("downButton");
leftButton.onclick = () => changeDirection(LEFT_DIR);
rightButton.onclick = () => changeDirection(RIGHT_DIR);
upButton.onclick = () => changeDirection(UP_DIR);
downButton.onclick = () => changeDirection(DOWN_DIR);
With this, you now have a perfect snake game! Try it out, and reach out to me if you have doubts, through my website rabiroshan.com!
有了这个,您现在有了一个完美的蛇游戏! 尝试一下,如果您有任何疑问,请通过我的网站rabiroshan.com与我联系!
Here you’ll find the complete project on my Github repository!
在这里,您可以在我的Github存储库中找到完整的项目!
Feel free to use the code!
随意使用代码!
贪吃蛇游戏