How to Make a Simple Snake Game in Javascript?
- 时间:2020-09-12 10:17:13
- 分类:网络文摘
- 阅读:85 次
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.

simple-snake-game-in-javascript
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) —
推荐阅读:食疗养生:治疗牙龈出血的食疗方 普通大豆粉摇身一变成为神奇保健品 食品添加剂:合法限量使用就没问题 武汉市场占八成豆制品来自小作坊 整治白酒“年份造假”乱象须标准先行 中医治疗牙周炎的常见饮食疗法 通过日常饮食疗法如何治疗牙周炎 炎炎夏日里清凉去火的十种食疗方 口腔食疗:牙龈“去火”的常见食物 贝因美营养米粉被指违规添加猪肝粉
- 评论列表
-
- 添加评论