How to Make a Simple Snake Game in Javascript?
- Time:2020-09-12 10:17:13
- Class:Weblog
- Read:25
Snake Game (snake eats apple and grows) is a simple and easy to implement game that you can practice when you learn a new programming language. It is not complex, and usually, it can be designed and completed in a day.
Drawing the Canvas
You would need a canvas to draw the snake (green body pieces) and the red apple. In the browser, you can define a Game canvas in HTML tag:
1 | <canvas width="400" height="400" id="game"></canvas> |
<canvas width="400" height="400" id="game"></canvas>
Then let’s define a few global variables that we need to use later:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | var context; var canvas; var score = 0; var bestscore = 0; var grid = 16; var count = 0; var snake = { x: 160, y: 160, // snake directin offsets dx: grid, dy: 0, // snake body cells: [], // snake body length, grows when eats an apple maxCells: 4 }; var apple = { x: 320, y: 320 }; |
var context; var canvas; var score = 0; var bestscore = 0; var grid = 16; var count = 0; var snake = { x: 160, y: 160, // snake directin offsets dx: grid, dy: 0, // snake body cells: [], // snake body length, grows when eats an apple maxCells: 4 }; var apple = { x: 320, y: 320 };
Game Controls
Then on the body onload event, we need to invoke a onload function:
1 | <body onload="windowload()"> |
<body onload="windowload()">
The onload function will be called once the HTML is loaded and the DOM is finished loading. Then, we listen to the keydown event to control the snake. movement.
The additional checks prevent snake from backtracking on itself by checking that it’s not already moving on the same axis (pressing left while moving left won’t do anything, and pressing right while moving left shouldn’t let you collide with your own body).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | function windowload() { canvas = document.getElementById('game'); canvas.setAttribute('tabindex','0'); canvas.focus(); context = canvas.getContext('2d'); // arrow keys to control the snake document.addEventListener('keydown', function(e) { // left arrow key if (e.which === 37 && snake.dx === 0) { snake.dx = -grid; snake.dy = 0; } // up arrow key else if (e.which === 38 && snake.dy === 0) { snake.dy = -grid; snake.dx = 0; } // right arrow key else if (e.which === 39 && snake.dx === 0) { snake.dx = grid; snake.dy = 0; } // down arrow key else if (e.which === 40 && snake.dy === 0) { snake.dy = grid; snake.dx = 0; } }); window.requestAnimationFrame(loop); } |
function windowload() { canvas = document.getElementById('game'); canvas.setAttribute('tabindex','0'); canvas.focus(); context = canvas.getContext('2d'); // arrow keys to control the snake document.addEventListener('keydown', function(e) { // left arrow key if (e.which === 37 && snake.dx === 0) { snake.dx = -grid; snake.dy = 0; } // up arrow key else if (e.which === 38 && snake.dy === 0) { snake.dy = -grid; snake.dx = 0; } // right arrow key else if (e.which === 39 && snake.dx === 0) { snake.dx = grid; snake.dy = 0; } // down arrow key else if (e.which === 40 && snake.dy === 0) { snake.dy = grid; snake.dx = 0; } }); window.requestAnimationFrame(loop); }
The window.requestAnimationFrame takes a function call back as a parameter that tells windows to run on painting the next frame. Before the main game loop, we need to define a few helper functions:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | // return a random integer between [min, max) function getRandomInt(min, max) { return Math.floor(Math.random() * (max - min)) + min; } function showScore(score) { document.getElementById('score').innerHTML = score; } function showBestScore(score) { document.getElementById('bestscore').innerHTML = score; } // reset the game function resetGame() { snake.x = 160; snake.y = 160; snake.cells = []; snake.maxCells = 4; snake.dx = grid; snake.dy = 0; score = 0; showScore(score); apple.x = getRandomInt(0, 25) * grid; apple.y = getRandomInt(0, 25) * grid; } |
// return a random integer between [min, max) function getRandomInt(min, max) { return Math.floor(Math.random() * (max - min)) + min; } function showScore(score) { document.getElementById('score').innerHTML = score; } function showBestScore(score) { document.getElementById('bestscore').innerHTML = score; } // reset the game function resetGame() { snake.x = 160; snake.y = 160; snake.cells = []; snake.maxCells = 4; snake.dx = grid; snake.dy = 0; score = 0; showScore(score); apple.x = getRandomInt(0, 25) * grid; apple.y = getRandomInt(0, 25) * grid; }
Main Game Loop
In the game loop, we need to recursively tell windows to requestAnimationFrame. Then, clear the canvas and draw the snake body pieces and the apple.
The game-over needs to be triggered if the snake hits the wall, or it collides with its body. When it moves, we can pop one from its tail and push it to the front (head).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | function loop() { requestAnimationFrame(loop); // slow game loop to 15 fps instead of 60 (60/15 = 4) if (++count < 4) { return; } count = 0; context.clearRect(0,0,canvas.width,canvas.height); // move snake by it's velocity snake.x += snake.dx; snake.y += snake.dy; if ((snake.x < 0) || (snake.x >= canvas.width)) { resetGame(); return; } if ((snake.y < 0) || (snake.y >= canvas.height)) { resetGame(); return; } // keep track of where snake has been. front of the array is always the head snake.cells.unshift({x: snake.x, y: snake.y}); // remove cells as we move away from them if (snake.cells.length > snake.maxCells) { snake.cells.pop(); } // draw apple context.fillStyle = 'red'; context.fillRect(apple.x, apple.y, grid-1, grid-1); // draw snake one cell at a time context.fillStyle = 'green'; snake.cells.forEach(function(cell, index) { // drawing 1 px smaller than the grid creates a grid effect in the snake body so you can see how long it is context.fillRect(cell.x, cell.y, grid-1, grid-1); // snake ate apple if (cell.x === apple.x && cell.y === apple.y) { snake.maxCells++; // canvas is 400x400 which is 25x25 grids apple.x = getRandomInt(0, 25) * grid; apple.y = getRandomInt(0, 25) * grid; score ++; bestscore = Math.max(bestscore, score); showBestScore(bestscore); showScore(score); } // check collision with all cells after this one (modified bubble sort) for (var i = index + 1; i < snake.cells.length; i += 1) { // snake occupies same space as a body part. reset game if (cell.x === snake.cells[i].x && cell.y === snake.cells[i].y) { resetGame(); return; } } }); } |
function loop() { requestAnimationFrame(loop); // slow game loop to 15 fps instead of 60 (60/15 = 4) if (++count < 4) { return; } count = 0; context.clearRect(0,0,canvas.width,canvas.height); // move snake by it's velocity snake.x += snake.dx; snake.y += snake.dy; if ((snake.x < 0) || (snake.x >= canvas.width)) { resetGame(); return; } if ((snake.y < 0) || (snake.y >= canvas.height)) { resetGame(); return; } // keep track of where snake has been. front of the array is always the head snake.cells.unshift({x: snake.x, y: snake.y}); // remove cells as we move away from them if (snake.cells.length > snake.maxCells) { snake.cells.pop(); } // draw apple context.fillStyle = 'red'; context.fillRect(apple.x, apple.y, grid-1, grid-1); // draw snake one cell at a time context.fillStyle = 'green'; snake.cells.forEach(function(cell, index) { // drawing 1 px smaller than the grid creates a grid effect in the snake body so you can see how long it is context.fillRect(cell.x, cell.y, grid-1, grid-1); // snake ate apple if (cell.x === apple.x && cell.y === apple.y) { snake.maxCells++; // canvas is 400x400 which is 25x25 grids apple.x = getRandomInt(0, 25) * grid; apple.y = getRandomInt(0, 25) * grid; score ++; bestscore = Math.max(bestscore, score); showBestScore(bestscore); showScore(score); } // check collision with all cells after this one (modified bubble sort) for (var i = index + 1; i < snake.cells.length; i += 1) { // snake occupies same space as a body part. reset game if (cell.x === snake.cells[i].x && cell.y === snake.cells[i].y) { resetGame(); return; } } }); }
If you allow snake to rewind to other side of the canvas, you can use the following code when checking the boundaries.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // wrap snake position horizontally on edge of screen if (snake.x < 0) { snake.x = canvas.width - grid; } else if (snake.x >= canvas.width) { snake.x = 0; } // wrap snake position vertically on edge of screen if (snake.y < 0) { snake.y = canvas.height - grid; } else if (snake.y >= canvas.height) { snake.y = 0; } |
// wrap snake position horizontally on edge of screen if (snake.x < 0) { snake.x = canvas.width - grid; } else if (snake.x >= canvas.width) { snake.x = 0; } // wrap snake position vertically on edge of screen if (snake.y < 0) { snake.y = canvas.height - grid; } else if (snake.y >= canvas.height) { snake.y = 0; }
The simple snake game can be played here: https://helloacm.com/static/game/snake/
Play Snake Game
Want to play the snake game? Here are two good options:
- Simple Snake Game in Javascript
- Chrome Extension: Snake Game
–EOF (The Ultimate Computing & Technology Blog) —
Recommend:Read This Before You Buy a Premium WordPress Theme
5 Trust Indicators And How To Use Them To Grow Your Blog
How to Merge k Sorted Lists using Recursive Divide and Conquer A
The String ZigZag Conversion Algorithms
How Experts Make Your Website Successful?
Replace Elements with Greatest Element on Right Side using C++ s
Compute the Deepest Leaves Sum of a Binary Tree using BFS or DFS
Microbit Programming: Snake Game with Growing Body and Greedy St
How to Make a Simple Snake Game in Javascript?
Integration Tests Using PHPUnit to Ensure Your Website is Workin
- Comment list
-
- Comment add