Search⌘ K
AI Features

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.

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:

  1. Add an event listener to the document object. The event will listen to the keydown event. This event will trigger a callback function each time a key is pressed.

  2. Handle the snake's direction based on the keyboard input.

First let's write a function to handle the keydown event.

Node.js
//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";
}

Explanation:

  • Lines 2–19: We create a function that will be called when a keydown event 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 right if any other key is pressed that is not being handled in the above if-else blocks.

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.

Node.js
// Function to move the snake to the horizontal right direction
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 is left
if(this.direction == "left"){
newX = rightX - 1;
newY = rightY;
}
// Update the new coordinates if the direction is down
else if(this.direction == "down"){
newX = rightX;
newY = rightY + 1;
}
// Update the new coordinates if the direction is up
else if(this.direction == "up"){
newX = rightX;
newY = rightY - 1;
}
// Update the new coordinates if the direction is 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;
}
}

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 by 1 and 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 by 1 and 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 by 1 and 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 1 and 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_over to be true.

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);
Final code to move the snake based on the keyboard input

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.js file:

    • Line 8: We initialize the variable game_over and set its initial value to false.

    • 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 document object. 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.