Move Snake Using Keyboard Input
Explore how to implement keyboard input controls in your JavaScript snake game project. Learn to use event listeners and keydown handlers to move the snake smoothly in all directions while managing game boundaries.
We'll cover the following...
Introduction
To recap, in the previous lesson, we could move the snake in the right-hand direction horizontally. Now we will move to the next step and make the game more realistic by adding the functionality to move the snake based on the keyboard input.
Implement: Move the snake with keyboard arrows
To move the snake based on the input provided from the keyboard, we need to follow the steps mentioned below:
Add an event listener to the
documentobject. The event will listen to thekeydownevent. This event will trigger a callback function each time a key is pressed.Handle the snake's direction based on the keyboard input.
First let's write a function to handle the keydown event.
Explanation:
Lines 2–19: We create a function that will be called when a
keydownevent occurs. The callback handler function will have access to the event object being passed as a parameter to the function.Lines 5–6: We check if the pressed key is a left-arrow key. If it is, then we set the snake's direction property to
left.Lines 9–10: We check if the pressed key is a down-arrow key. If it is, then we set the snake's direction property to
down.Lines 13–14: We check if the pressed key is an up-arrow key. If it is, then we set the snake's direction property to
up.Lines 17–18: We set the snake's direction property to
rightif any other key is pressed that is not being handled in the aboveif-elseblocks.
Now let's update the updateSnake() function we created in the previous lesson to make the snake's movement based on the keyboard input.
Note: Before moving on, a quick point to remember is that the x-axis starts from the top left corner of the screen and moves horizontally rightwards, whereas the y-axis starts from the top left corner of the screen and moves vertically downwards.
Explanation:
The code is similar to what we discussed in the previous lesson, but now we update the coordinates differently. Let's go over the differences.
Lines 14–17: We check if the direction property of the snake is set to
left. If it is, then we decrease the x-coordinate by1and make no change in the y-coordinate, because we want the snake to move to the left. Hence, the rightmost block of the snake would be at position (x - 1, y).Lines 20–23: We check if the direction property of the snake is set to
down. If it is, then we increase the y-coordinate by1and make no change in the x-coordinate, because we want the snake to move down. Hence, the rightmost block of the snake would be at position (x, y + 1).Lines 26–29: We check if the direction property of the snake is set to
up. If it is, then we decrease the y-coordinate by1and make no change in the x-coordinate, because we want the snake to move up. Hence, the rightmost block of the snake would be at position (x, y - 1).Lines 32–35: We move the snake to the default direction (right). To do this, we increase the x-coordinate by
1and make no change in the y-coordinate, because we want the snake to move to the right. Hence, the rightmost block of the snake would be at position (x + 1, y).Lines 41–42: We store the number of blocks that can be accommodated in the canvas. For the maximum number of blocks in a horizontal direction, we can divide the width of the canvas by each block size (in line 41), whereas to find the maximum number of blocks in a vertical direction, we can divide the height of the canvas by each block size (in line 42).
Lines 45–46: We check if the snake's head either becomes less than 0 (reaches the leftmost boundary or the topmost boundary) or reaches the maximum number of blocks that can be allowed in the canvas (reaches the rightmost boundary or the bottommost boundary). If it does, we set the flag
game_overto betrue.
Now let's merge the above functions with our main JavaScript code. While doing so, we will first increase the width and height of the canvas. This is done because when the output appears, the snake starts moving in the right direction, and it gives little time for the user to press the arrow keys to move the snake. We also need to make a message print when the game is over.
Note: If you cannot move the snake with your keyboard input, try to open the app URL in a separate browser's tab and then play the game.
// The first step in the game loop
function init(){
canvas = document.getElementById('snake-game');
H = canvas.height = 500;
W = canvas.width = 1000;
pen = canvas.getContext('2d');
blockSize = 66;
game_over = false;
snake = {
length: 2, // number of rectangles making up the snake
color: "blue", // the color of the snake
cells: [], // the positions of each of the rectangle
direction: "right", // the direction in which the rectangle moves
// Create the snake by initializing the coordinates
createSnake: function() {
for(let i = this.length ; i > 0; i--)
// Add the (x, 0) coordinates to the cells array
this.cells.push({x: i, y: 0});
},
// Draw the snake on the HTML canvas
drawSnake: function() {
for(let i = 0; i < this.cells.length; i++){
// Set the color of the snake
pen.fillStyle = this.color;
// Draw rectangle by a margin of 3 so that each rectangle is separated from its adjacent
pen.fillRect(this.cells[i].x * blockSize, this.cells[i].y * blockSize, blockSize - 3, blockSize - 3);
}
},
// Update the snake by moving it based on the keyboard input
updateSnake: function() {
// Remove the last coordinates from the cells array (pointing to the leftmost block)
this.cells.pop();
// Store the coordinates of the rightmost block of the snake
var rightX = this.cells[0].x;
var rightY = this.cells[0].y;
var newX,newY;
// Update the new coordinates if the direction if left
if(this.direction == "left"){
newX = rightX - 1;
newY = rightY;
}
// Update the new coordinates if the direction if down
else if(this.direction == "down"){
newX = rightX;
newY = rightY + 1;
}
// Update the new coordinates if the direction if up
else if(this.direction == "up"){
newX = rightX;
newY = rightY - 1;
}
// Update the new coordinates if the direction if right
else{
newX = rightX + 1;
newY = rightY;
}
// Add the new coordinates to the cells array
this.cells.unshift({x: newX, y: newY});
// Get the number of blocks that are present in the canvas
var last_x = Math.round(W/blockSize);
var last_y = Math.round(H/blockSize);
// Stop the game when the snake reaches the boundary of the canvas
if(this.cells[0].y<0 || this.cells[0].x < 0 || this.cells[0].x > last_x || this.cells[0].y > last_y){
game_over = true;
}
}
}
// Create the Snake
snake.createSnake();
//Add an Event Listener on the Document Object
function directionHandler(e){
// If left arrow is pressed, then set the direction to be left
if(e.key=="ArrowLeft")
snake.direction = "left";
// If down arrow is pressed, then set the direction to be down
else if(e.key=="ArrowDown")
snake.direction = "down";
// If up arrow is pressed, then set the direction to be up
else if(e.key=="ArrowUp")
snake.direction = "up";
// If any other key is pressed, then set the direction to be right
else
snake.direction = "right";
}
// Add the event listener to the document object
document.addEventListener('keydown', directionHandler) ;
}
// The draw function for the game loop
function draw(){
pen.clearRect(0,0,W,H); // Clear the canvas so that previous blocks of snake are erased
snake.drawSnake();
}
// The update function for the game loop
function update(){
snake.updateSnake();
}
// The game loop function
function gameloop(){
// Handle when the game is over
if(game_over == true){
clearInterval(f);
paragraph = document.getElementById("game_status");
paragraph.innerText = `Game Over!`;
return;
}
draw();
update();
}
// Call the init() method to initialize the game
init();
// Execute the game loop at an interval of 100 milliseconds
var f = setInterval(gameloop, 200);Explanation:
We will only explain the highlighted code since the rest of the code is the same as we discussed in the previous lessons. In the
game_logic.jsfile:Line 8: We initialize the variable
game_overand set its initial value tofalse.Lines 36-84: We add the
updateSnake()function we wrote in the previous step.Lines 91–109: We add the event callback handler function.
Line 112: We add an event listener to the
documentobject. This is done to ensure that the snake should move according to your key press when you open the web page and press a key while your cursor is anywhere on the web page.Lines 130-135: We check if the game is over in every loop. If the game is over, we access the paragraph element from the HTML page and add a simple text message.