DEV Community

Cover image for JavaScript Hungry Caterpillar Game
Petrina Ropra
Petrina Ropra

Posted on

JavaScript Hungry Caterpillar Game

Intro: A small game like the famous Nokia snake game. You can pause the game too by pressing the spacebar on the keyboard. So it's a game where the user controls the caterpillar to eat a leaf and in doing so the user gets a point that adds to the score, the higher the score the faster the caterpillar moves. If the user collides with the canvas border the game ends. This is for complete beginners as I have commented on each line of code on what it does. Please let me know if I have made any mistakes. Thanks.

Watch Demo Here: https://youtube.com/shorts/kxzC3YCeSZI?feature=share

Tweak: You can change the colors, or change the speed of the caterpillar, or how much score will be increased whenever the caterpillar eats the leaf

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Snake Game</title>
    <link rel="stylesheet" href="hungry_snake.css">
</head>
<body>
<div class="game-container">
    <h1> Hungry Caterpillar</h1>
    <canvas id="gameCanvas"></canvas>
    <div id="startScreen">
        <h1>Press SPACE to Start</h1>
    </div>
    </div>
    <script src="hungry_caterpillar.js"></script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode
@font-face {
    font-family: 'PoetsenOneRegular';
    src: url('Fonts/PoetsenOne-Regular.ttf') format('truetype');
    font-weight: normal;
    font-style: normal;
}

body {
    margin: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
}

#game-container{
    text-align: center;
}

canvas {
    border: 1px solid black;
    background-color: lightgreen;
    display:  block;
}

h1{
    font-family: 'Poor Richard Regular', sans-serif;
    color: darkgreen;
    text-align: center;
}

#startScreen {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    text-align: center;
    font-family: Arial, sans-serif;
    font-size: 24px;
}

Enter fullscreen mode Exit fullscreen mode
//a const variable is immutable meaning its va;ue cannot be reassigned
//create a variable called canvas that stores the html canvas
const canvas = document.getElementById('gameCanvas');
//ctx gets a method that returns a 2d drawing context for the canvas
const ctx = canvas.getContext('2d');
//startScreen gets the startScreen div element in the html file
//basically it stores the div with the id-startScreen 
const startScreen = document.getElementById('startScreen');

//set the width of the canvas to 600
canvas.width = 600;
//set the height of the canvas to 400
canvas.height = 400;

//creata an array called caterpillar
//this array will be used to creat the body of the caterpillar
let caterpillar = [];
//set the length of the caterpillar
let caterpillarLength = 15;
//set the speed of the caterpillar to 10
let caterpillarSpeed = 10;
//set the score to zero
let score = 0;
//set the state of the game 
let gameStarted = false;
//set the caterpillar to move to the right
let dx = caterpillarSpeed;
//set the horizontal movement of the caterpillar to zero
let dy = 0;
//add paused flag
let paused = false

//create function to create the caterpillar
function createCaterpillar() {
    //create a loop that loops for as much as the value of caterpillarLength
    //so in this case it will loop through 15 times which is the value of caterpilarLength
    for (let i = 0; i < caterpillarLength; i++) {
        //everytime the loop loops a small circle will be pushed into the caterpillar array
        //the x object is the horizontal position of the circle dots and 
        //the y object is the vertical position of the circle dots
        //so basically small circles will be created one after another 15 times and put 
        //in the caterpillar array so the 3 is the space between each circle
        caterpillar.push({ x: canvas.width / 2 - i * 4, y: canvas.height / 2 });
    }
}

//an object called leaf is created and its properties are its 
//horizontal(x) and vertical(y) position on the canvas 
//it just appears at random positions in the canvas
let leaf = {
    x: Math.floor(Math.random() * (canvas.width - 30)),
    y: Math.floor(Math.random() * (canvas.height - 30))
};

//create a function drawLeaf() to draw the leaf
function drawLeaf() {
    //give the leaf a green color
    ctx.fillStyle = 'green';
    //start drawing the leaf
    ctx.beginPath();
    // Start point (left center of the leaf)
    ctx.moveTo(leaf.x, leaf.y + 10);  
    //draw top curve of leaf
    ctx.bezierCurveTo(leaf.x, leaf.y, leaf.x + 20, leaf.y, leaf.x + 30, leaf.y + 10);
    //draw bottom curve of leaf
    ctx.bezierCurveTo(leaf.x + 20, leaf.y + 20, leaf.x, leaf.y + 20, leaf.x, leaf.y + 10);
    //fill the leaf with hreen
    ctx.fill();
}

//create a function drawCaterpillar() to draw the caterpillae
function drawCaterpillar() {
    //this creates the head of the caterpillar
    //so the caterpillar's head will be black
    ctx.fillStyle = 'black';
    //begin drawing
    ctx.beginPath();
    //create the first circle of the caterpillar array
    ctx.arc(caterpillar[0].x + 10, caterpillar[0].y + 10, 10, 0, Math.PI * 2);
    //make the first circle black
    ctx.fill();
    //for the rest of the circles they'll be brown
    ctx.fillStyle = '#80461B';
    //create a loop to loop through the caterpillar array 
    for (let i = 1; i < caterpillar.length; i++) {
        //begin drawing
        ctx.beginPath();
        //draw the rest of the circles in the caterpillar array
        ctx.arc(caterpillar[i].x + 9, caterpillar[i].y + 9, 9, 0, Math.PI * 2);
        //all the other circles which is the carterpillar's body will be brown
        ctx.fill();
    }
}

//create a function called updateCaterpillar()
//this will update the pistion of the caterpillar as it moves
function updateCaterpillar() {
    //an object called head is creeted
    //the properties of this object positions the head of the caterpillar
    const head = { x: caterpillar[0].x + dx, y: caterpillar[0].y + dy };
    //put the head object as the first item of the caterpillar array
    caterpillar.unshift(head);

    //if head's horizontal position is less than the leaf's horizontal position plus 30 and
    if (head.x < leaf.x + 30 &&
        //if head's horizontal position is plus 20 is more than the leaf's horizontal position and
        head.x + 20 > leaf.x &&
        //if head's vertical position is less than the leaf's vertical position plus 20 and
        head.y < leaf.y + 20 &&
        //if head's vertical position plus 20 is less than the leaf's vertical poistion
        //basically whenever the caterpillar eats the leaf
        head.y + 20 > leaf.y) {
        //call the placeLeaf() function to move the leaf to a new location
        placeLeaf();
        //add 10 to score
        score += 10;
        //increase the caterpillar's speed by 0.5
        caterpillarSpeed += 0.5;
    } else {
        //remove the last segment of the caterpillar
        caterpillar.pop();
    }
}

//create a function called placeLeaf()
//this function basically moves the leaf to a new random location
//whenever the caterpillar eats it.
function placeLeaf() {
    leaf.x = Math.floor(Math.random() * (canvas.width - 30));
    leaf.y = Math.floor(Math.random() * (canvas.height - 20));
}

//create a function called isGameOver()
function isGameOver() {
    //create a variable called head that stores the head of the caterpillar
    //which is the first circle in the caterpillar array
    const head = caterpillar[0];
    //Check if the head is outside the canvas boundaries
    if (head.x < 0 || head.x >= canvas.width || head.y < 0 || head.y >= canvas.height) {
        //If any condition is true, the head is out of bounds, so return true
        return true;
    }
    //create a loop that loops through the caterpillar's body or caterpillar array
    for (let i = 1; i < caterpillar.length; i++) {
        //Check if head is at the same position as the current segment of the caterpillar
        if (head.x === caterpillar[i].x && head.y === caterpillar[i].y) {
            //if both the x and y coordinates match, return true to indicate a collision
            return true;
        }
    }
    //if there are no collisions return false
    return false;
}

//create a function called gameOver()
function gameOver() {
    //choose yellow as the fill color
    ctx.fillStyle = 'yellow';
    //fill the entire canvas with yellow
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    //choose black as the fill color for the text
    ctx.fillStyle = 'black';
    //set the font size to 30 pixels and use Arial font
    ctx.font = '30px Arial';
    //center the text horizontally
    ctx.textAlign = 'center';
    //draw the text "GAME OVER", canvas.width / 2, canvas.height / 2);
    ctx.fillText('GAME OVER!', canvas.width / 2, canvas.height / 2);
}

//create a function called drawScore()
function drawScore() {
    //choose black as the fill color
    ctx.fillStyle = 'black';
    //set the font size to 20 pixels and use Arial font
    ctx.font = '20px Arial';
    //display the score on the canvas 
    //set the position of the score at the top right corner of the canvas
    //set the font size of the score to 30 pixels
    ctx.fillText('Score: ' + score, canvas.width - 100, 30);
}

//create a function called clearCanvas()
function clearCanvas() {
    //clear the entire canvas, removing all previously drawn content
    ctx.clearRect(0, 0, canvas.width, canvas.height);
}

//create a function called gameLoop()
function gameLoop() {
    //if the isGameOver() function returns true
    if (isGameOver()) {
        //call the gameOver() function
        gameOver();
        //exit current function
        return;
    }
    //if game is paused stop game
    if(paused) return;

    //call the clearCanvas() function to clear the canvas
    clearCanvas();
    //call the drawLeaf() function to draw the leaf
    drawLeaf();
    //call the updateCaterpillar() function
    updateCaterpillar();
    //call the drawCaterpillar() function
    drawCaterpillar();
    //call the drawScore function()
    drawScore();

    //set a timeout to call requestAnimationFrame with gameLoop after a delay based on caterpillarSpeed
    setTimeout(() => {
        //request the browser to call gameLoop to update the animation
        requestAnimationFrame(gameLoop);
        //the request will be carried out after 
        //1000miliseconds divided by the value of caterpillarSpeed 
    }, 1000 / caterpillarSpeed);
}

//create a function called startGame
function startGame() {
    //if the value of gameStarted is true
    //the ! indicates that gameStarted is not false 
    //which was the initial value of gameStarted
    if (!gameStarted) {
        //gameStarted is now set to true
        gameStarted = true;
        //hide the start screen by setting its display style to 'none'
        startScreen.style.display = 'none';
        //call createCaterpillar() function
        createCaterpillar();
        //call gameLoop()
        gameLoop();
    }
}

//create a function called togglePause()
function togglePause() {
    //the variable paused is now set to true
    paused = !paused;
    //if true
    if (!paused) {
        // Resume the game loop if unpaused
        gameLoop(); 
    }
}

//add an event listener for keydown events
document.addEventListener('keydown', event => {
    //if the spacebar is pressed
    if (event.code === 'Space') {
        //if gameStarted is true start game
        if(!gameStarted) {
            startGame();
        //if gameStarted is false
        } else {
            //call the togglePause() function
            //or basically pause game
            togglePause();
        }
    }

    if(!paused){
        //if the up arrow is pressed and the caterpillar is not moving
        //vertically, move up
        if (event.code === 'ArrowUp' && dy === 0) {
            dx = 0;
            dy = -caterpillarSpeed;
        }

        //if the down arrow is pressed and the caterpillar is not moving vertically,
        //move down
        if (event.code === 'ArrowDown' && dy === 0) {
            dx = 0;
            dy = caterpillarSpeed;
        }

        //if the left arrow is pressed and the caterpillar is not moving horizontally,
        //move left
        if (event.code === 'ArrowLeft' && dx === 0) {
            dx = -caterpillarSpeed;
            dy = 0;
        }

        //if the left arrow is pressed and the caterpillar is not moving horizontally,
        //move right
        if (event.code === 'ArrowRight' && dx === 0) {
            dx = caterpillarSpeed;
            dy = 0;
        }
    }
});

Enter fullscreen mode Exit fullscreen mode

Top comments (0)