Develop a simple snake game using vanilla javascript, html and css. Show Demo
NOTE: READ COMMENTS TO UNDERSTAND THE CODE ✌🏻
NOTE: YOU CAN SKIP TO STEP 4 TO READ THE ACTUAL CODE IN JS
LET US START:
- Create the the base files:
- index.html
- main.css
- jscript.js
-
Add the following content to
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 main.css --> <link rel="stylesheet" type="text/css" href="main.css" /> </head> <body> <!-- #gameContainer is the main game board--> <div class="gameContainer" 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="mobileControllers"> <button id="leftButton">◀️</button> <div> <button id="upButton">🔼</button> <button id="downButton">🔽</button> </div> <button id="rightButton">▶️</button> </div> </body> <!-- #Load our jscript.js containing the game logic --> <script src="jscript.js"></script> </html>
-
Add the following content to
main.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 { /* 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 STYLES*/ #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; } }
-
Finally, we will write the JavaScript code in
jscript.js
where the actual logic is written.-
Initialize the scores:
let totalFoodAte = 0; let totalDistanceTravelled = 0;
-
Code for the Game Board pixels:
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");
-
Code for the Food:
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"); };
-
Code for the Snake 🐍:
-
Code related to the Direction of 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; } };
-
Code related to the Movement of the snake:
// 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(); } };
-
-
Code to start the game using the above logic:
// 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);
-
Top comments (0)