Search⌘ K
AI Features

Add Food for the Snake

Understand how to implement food generation at random positions on the game canvas and update the snake's length and score when it eats the food. This lesson guides you through modifying game logic to track score, display food images, and update the game interface for an interactive snake game experience.

Introduction

In the previous lesson, we moved the snake using the keyboard input. Now, we will move to the next step of adding food for the snake and incrementing the score each time the snake eats the food object. Whenever the snake eats food from the board, the size of the snake is increased by 1 block. We will add an image of the food and display it randomly on the board for the snake to eat.

Implement: Add the food randomly on the board and increment the score

To add the food and increase the length of the snake by 1 block each time the snake eats the food, we need to follow the steps mentioned below:

  • Create a function to generate the random coordinates for the food object to be displayed at on the canvas.

  • Modify the updateSnake() function to increment a variable score that keeps track of the number of food objects eaten by the snake and adjust the snake length.

First let's see the function to generate the food object coordinates randomly. While doing so, we must ensure that the coordinates generated are within the canvas height and width range.

Node.js
// Function to generate the coordinates of the food object randomly
function generateFoodCoordinates(){
// Generate the x and y coordinates randomly while ensuring the range is within
// the canvas width and height
var foodXCoordinate = Math.round(Math.random()*(W - blockSize) / blockSize);
var foodYCoordinate = Math.round(Math.random()*(H - blockSize) / blockSize);
// Create the food object
var food = {
x: foodXCoordinate,
y: foodYCoordinate,
color:"red",
}
// Return the food object
return food
}

Explanation:

  • Lines 2–18: We create a function to generate the coordinates for the food object randomly.

    • Lines 6–7: We calculate the x-coordinate and y-coordinate for the food object, respectively. This is done by subtracting the block size from the height and width and then dividing it by the block size to get the coordinates.

    • Lines 10–14: We create an object for the food, containing the coordinates of the point where the food object will be displayed on the canvas, as well as the color of the food.

    • Line 17: We return the food object.

Now let's modify the updateSnake() function to increment the score each time the snake eats the food object.

Node.js
// Update the snake by moving it horizontally to the right
updateSnake: function() {
// Store the coordinates of the rightmost block of the snake
var rightX = this.cells[0].x;
var rightY = this.cells[0].y;
// If the snake reaches the food coordinate
if(rightX == food.x && rightY == food.y){
food = generateFoodCoordinates();
score++;
}
else{
// Remove the last coordinates from the cells array (pointing to the leftmost block)
this.cells.pop();
}
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;
}
}

Explanation:

Now let's go over the highlighted code (the rest of the code is the same as we discussed in the previous lesson.)

  • Lines 9–12: We check if the snake's head has reached the food object's coordinates. If the condition is true, then we call the generateFoodCoordinates() function to get new coordinates for the food object and increment the score since the snake has eaten up one food object.

  • Lines 13–16: If the condition is false, then we remove the leftmost block of the snake (since the snake has not eaten anything, the length of the snake should not increase).

Now let's merge the above functions with our main JavaScript code. While doing so, we also need to create a score variable to track the number of food objects eaten by the snake and an Image object to display the food on the board. Also, we need to draw the food object inside the draw() function.

Now let's see the final code for the snake game.

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;
	
	score = 2; // to keep track of the score of the user
	
	//Create an Image object for food
	food_img = new Image();
	food_img.src = "./snake_food.png";

	food = generateFoodCoordinates();

	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 horizontally to the right
		updateSnake: function() {

			// Store the coordinates of the righmost block of the snake 
			var rightX = this.cells[0].x;
			var rightY = this.cells[0].y;

			// If the snake reaches the food coordinate
			if(rightX == food.x && rightY == food.y){
				food = generateFoodCoordinates();
				score++;
			}
			else{
				// Remove the last coordinates from the cells array (pointing to the leftmost block)
				this.cells.pop();
			}

			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});

			var last_x = Math.round(W/blockSize);
			var last_y = Math.round(H/blockSize);

			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";
	}

	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();

	// Draw the food image on the canvas
	pen.fillStyle = food.color;
	pen.drawImage(food_img,food.x * blockSize, food.y * blockSize, blockSize, blockSize);

	// Display the current score to the user
	paragraph = document.getElementById("game_status");
	paragraph.innerText = `Score: ${score}`;
}

// The update function for the game loop
function update(){
	snake.updateSnake(); 
}

// The game loop function
function gameloop(){
	if(game_over==true){
		clearInterval(f);
		paragraph = document.getElementById("game_status");
		paragraph.innerText = `Game Over! Your score is: ${score}`;
		return;
	}
	draw();
	update();
}

// Function to generate the coordinates of the food object randomly
function generateFoodCoordinates(){

  // Generate the x and y coordinates randomly while ensuring the range to be withing
  // the canvas width and height 
	var foodXCoordinate = Math.round(Math.random()*(W - blockSize) / blockSize);
	var foodYCoordinate = Math.round(Math.random()*(H - blockSize) / blockSize);

  // Create the food object
	var food = {
		x: foodXCoordinate,
		y: foodYCoordinate,
		color:"red",
	}

  // Return the food object
	return food
}

// 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 for the snake game project

Explanation:

Let's go over the highlighted code (the rest of the code is the same as the one we discussed in the previous lesson.)

In the game_logic.js file:

  • Line 10: We define a variable to track the number of food objects eaten by the snake. The value is initialized by 2 since the snake's initial length is also 2.

  • Lines 13–14: We create an Image object and set the src property to be the location of the food's image.

  • Line 16: We call the generateFoodCoordinates() function to get the random coordinates of the food object.

  • Lines 51–59: We add the logic to increment the score when the snake's head reaches the coordinates of the food.

  • Lines 133–134: We modify the draw() function to draw the image of the food on the canvas.

  • Lines 137–138: We access the paragraph element from the HTML and add text to display the current score to the user.

  • Lines 158–175: We add the generateFoodCoordinates() function that we created in the previous step.

This marks the end of this project. We hope you have enjoyed building this simple yet exciting game using arrays and objects in JavaScript!