A Basic Game In Javascript: Cat and Dog

This past year, I introduced my students to Javascript. I created this simple game called Cat and Dog to introduce the basics of a game loop and getting basic input and movement in a game.

This is a 2 player game where the cat is controlled by the keyboard and the dog is controlled by the mouse. The objective of the game is for the dog to catch the cat.

The following keys control the cat (blue circle):

  • w: moves the cat up
  • s: moves the cat down
  • a: moves the cat left
  • d: moves the cat right

The dog (green circle) is controlled by the mouse.

The finalized game looks like this below:

Note: If you are on a Linux distribution like Ubuntu, you might not be able to move the cat and dog at the same time as there is a default setting to disable the mouse while the keyboard is being used. Refer to this Stack Exchange question to enable mouse while using keyboard.

I am going to go step by step on how to create the game. I will assume you are familiar with HTML5 and Javascript.

Table of Contents

Creating the HMTL page and the Canvas

The first thing we're going to do is to create the HTML page and insert the canvas into the body. The canvas will have the id gameArea and it's going to be 800x600 and will have a border around it.

We're also going to add a script section after the canvas. That is where all the javascript code will go.

In a text editor (such as notepad), you can add the following code.

<!DOCTYPE html>
<html>
<head>
<title>Cat and Dog</title>
<style>
#gameArea{
  border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="gameArea" width="800" height="600"></canvas>
<script>
</script>
</body>
</html>

Save your code as CatAndDog.html and open it in your web browser. If you've typed that all correctly, you should see a blank rectangle on your screen.

Setting Up The Basic Game Loop

The basic game loop for most games go something like this:

  1. Update game data
  2. Draw game
  3. Repeat step 1 and 2 until something happens for the game to end (ie. you win/lose)

To organize our code so it's easier to read and debug, we will create 3 functions.

The first function is the update function. This function is responsible for checking and updating the various information in the game. Such information includes position of game objects, collision of game objects, etc.

The second function is the draw function. This function is responsible for drawing the game onto the screen. We call the draw function after we cal the update function so that the game is drawn with the most up to date information.

The last function is our main function. This function calls the update and the draw function. To repeat, the function basically calls itself using the built-in requestAnimationFrame function in Javascript.

All Javascript code will go in between <script> and </script> tags.

Thfe code for the functions are shown below.

function update()
{
}

function draw()
{
}

function main()
{
  update();
  draw();

  // call the main function again to create the loop
  window.requestAnimationFrame(main);
}

// call the main function to start the game
window.requestAnimationFrame(main);

After you enter the code above between <script> and </script>, you should still see the same rectangle as before. Nothing is happening since the update and draw function are empty. It should look like the rectangle below.

For your reference, in case you are confused of where to put the Javascript, this is what you should have in your HTML file.

<!DOCTYPE html>
<html>
<head>
<title>Cat and Dog</title>
<style>
#gameArea{
  border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="gameArea" width="800" height="600"</canvas>
<script>
function update()
{
}

function draw()
{
}

function main()
{
  update();
  draw();

  // call the main function again to create the loop
  window.requestAnimationFrame(main);
}
window.requestAnimationFrame(main);
</script>
</body>
</html>

Draw the Cat and Dog

The cat and dog will be represented by circles on the screen. Before we draw the cat and dog onto the screen, we are going to set up some variables so the computer can keep track of some data about the cat and dog.

The computer needs to know is the size of the circle in order to draw it. All the computer needs to know is the radius of the circle.

The computer must also know position of the cat and dog are on the screen. We will use the center of the circle to describe the position. We can describe this to the computer using a coordinate point. Like what you've learned in math, a coordinate point has an x and y value where x is the horizontal position and y is the vertical position.

The top left corner of the canvas is the origin (0,0). The values of x increase as you move to the right so the point (10,0) will be to the right of the origin. The values of y are a bit different than what you learned in math. As you go down the canvas, the y values increase. So the point (0,10) will be below the origin.

So let's set up some variables so to keep track of the data we just mentioned above. We will put these right after the script tag and before the functions we created in the previous section. We will put the cat and dog at opposite corners of the canvas.

// cat variables
var catX = 100; // the x position of the cat
var catY = 100; // the y position of the cat
var catSize = 20; // the radius of the cat is 20 pixels

// dog variables
var dogX = 700; // the x positon of the dog
var dogY = 500; // the y position of the dog
var dogSize = 20; // the radius of the dog is 20 pixels

For your reference, your script section should now look like this:

<script>
// cat variables
var catX = 100; // the x position of the cat
var catY = 100; // the y position of the cat
var catSize = 20; // the radius of the cat is 20 pixels

// dog variables
var dogX = 700; // the x positon of the dog
var dogY = 500; // the y position of the dog
var dogSize = 20; // the radius of the dog is 20 pixels

function update()
{
}

function draw()
{
}

function main()
{
  update();
  draw();

  // call the main function again to create the loop
  window.requestAnimationFrame(main);
}

// call the main function to start the game
window.requestAnimationFrame(main);
</script>

To display the cat and dog on the screen, we need to tell the computer two things.

  1. Which canvas are we going to draw on
  2. The actual command to draw the circle

So first, let's tell the computer the canvas we will draw on. We can specify the canvas by using the id of the canvas which in our case is gameArea. From there, we will set a 2D context since our game is in 2D which in our program we will call it ctx.

Add the following code after the dog variables and before the functions.

// canvas properties
// identify the correct canvas
var canvas = document.getElementById("gameArea"); 
var ctx = canvas.getContext("2d"); // get the 2D context

After you add the above, your code should look like below (some parts are hidden to save space)

// code before not shown to save space

// dog variables
var dogX = 700; // the x positon of the dog
var dogY = 500; // the y position of the dog
var dogSize = 20; // the radius of the dog is 20 pixels

// canvas properties
// identify the correct canvas
var canvas = document.getElementById("gameArea"); 
var ctx = canvas.getContext("2d"); // get the 2D context

function update()
{
}


// code above not shown to save space

Finally, we can issue the draw commands to the computer in the draw function to draw the circles. The main command is the function arc(xPosition, yPosition, radius, startAngle, endAngle); from the canvas context, ctx.

The function arc draws arc of a circle. That means it can draw partial circles from a start angle to an end angle. The angles are specified with radians. The arc function needs 5 parameters (values) in order to draw a circle. First, it needs to know the x and y position of where to draw, the radius of the circle, and what angle do you want to start drawing the circle from. Since we're drawing the whole circle, we can start at angle 0 and draw all the way to 2π.

So our line of code to draw our cat will look like below:

// draw the cat
ctx.arc(catX, catY, catSize, 0, 2*Math.PI);

However, before you paste that in, the context can draw very complex shapes using paths. The arc function adds a path to the context to draw. To stop paths from overlapping, we must explicitly tell the context to start a new path. To see the resulting shape, we must either fill the path or outline the path with a color.

So we will add the following code into the draw function for the cat.

// draw cat
ctx.fillStyle="#0000FF"; // our cat will be blue (hexadecimal colors)
ctx.beginPath(); // start a new path
ctx.arc(catX, catY, catSize, 0, 2*Math.PI); // specify the arc
ctx.fill(); // fill the area the circle encloses with blue

It will be similar for the dog (which will be green), so our draw function with the new code added in will look as follows:

function draw()
{
  // draw cat
  ctx.fillStyle="#0000FF"; // cat will be blue (hexadecima colors)
  ctx.beginPath(); // start a new path
  ctx.arc(catX, catY, catSize, 0, 2*Math.PI); // specify the arc
  ctx.fill(); // fill the area the circle encloses with blue

  // draw dog
  ctx.fillStyle="#00FF00"; // dog will be green (hexadecimal colors)
  ctx.beginPath(); // start a new path
  ctx.arc(dogX, dogY, dogSize, 0, 2*Math.PI); // specify the arc
  ctx.fill(); // fill the area the circle encloses with green
}

Your game should now look like the canvas below

Moving the Dog

The dog is controlled by the mouse. The dog will be wherever the mouse pointer is. In order to find out where the mouse is, we must set an action listener to listen for actions that happen to the mouse such as mouse movement, mouse click, etc.

Since we just want to move the dog with the motion of the mouse instead of clicking, we'll set an action listener to our canvas to detect when the mouse moves.

Add the following line to our code right after we set the 2D context

// capture mouse event
canvas.addEventListener("mousemove", moveDog);

The action we're keeping track is the movement of the mouse which is specified as "mousemove". When the mouse moves, we want to call the moveDog function which we will create next.

With the line added, your code should look like this

// code before not shown to save space

var ctx = canvas.getContext("2d"); // get the 2D context

// capture mouse event
canvas.addEventListener("mousemove", moveDog);

function update()

// code after not shown to save space

Now we can create the moveDog function that will update the dogX and dogY to the x and y positions of the mouse pointer. You can add this function before the update function. The code is below.

function moveDog(event)
{
  var canvasRect = canvas.getBoundingClientRect();
  dogX = event.clientX - canvasRect.left;
  dogY = event.clientY - canvasRect.top;
}

The moveDog function has a parameter called event that holds all the information about the mouse action. As you may have guessed, event.clientX returns the x position of the mouse while event.clientY returns the y position of the mouse.

So you may be asking yourself, why is it not just simply dogX = event.clientX?

The problem is that event.clientX returns the x position of the mouse in terms of your entire screen where (0,0) is the top left corner of your screen and not your canvas. So we need to adjust that value by making it relative to our canvas.

canvas.getBoundingClientRect() gets us the position information of the canvas in terms of the entire screen. canvasRect.left returns the x position of the top left corner in relation to our entire screen coordinate. Since our pointer must be in the canvas to move the mouse, the x position of the pointer must be greater than canvasRect.left. Thus if we minus canvasRect.left from event.clientX, we get the position of the pointer in terms of the canvas coordinate.

The same is similar for the y value.

After adding the moveDog function, your code should look like below

// code before not shown to save space

// capture mouse event
canvas.addEventListener("mousemove", moveDog);

function moveDog(event)
{
  var canvasRect = canvas.getBoundingClientRect();
  dogX = event.clientX - canvasRect.left;
  dogY = event.clientY - canvasRect.top;
}

function update()

// code after not shown to save space

Your mouse should now be able to move the dog which is the green circle like in the demo below.

Clearing the Screen

The dog moves! But you seem to be creating many dogs instead of just moving the dog. Looks more like a painting program. What's going on?

When we moved the dog, the game drew the dog at the new position. However, since the game already drew the dog at the old position, it remains on the screen. What we have to do is to erase everything on the screen so the things in the old position disappear and we are only left with the things at their current positions.

To do this, we simply just draw a white rectangle over the entire canvas before we draw the new dog and cat.

We'll update the draw function with the highlighted code below.

function draw()
{
  // clear canvas by painting the whole canvas white
  ctx.fillStyle="white";
  ctx.fillRect(0,0,800,600);
  
  // draw cat
  ctx.fillStyle="#0000FF"; // cat will be blue (hexadecima colors)
  ctx.beginPath(); // start a new path
  ctx.arc(catX, catY, catSize, 0, 2*Math.PI); // specify the arc
  ctx.fill(); // fill the area the circle encloses with blue

  // draw dog
  ctx.fillStyle="#00FF00"; // dog will be green (hexadecimal colors)
  ctx.beginPath(); // start a new path
  ctx.arc(dogX, dogY, dogSize, 0, 2*Math.PI); // specify the arc
  ctx.fill(); // fill the area the circle encloses with green
}

The fillRect function takes 4 parameters

  1. the x position of the top left corner of the rectangle
  2. the y position of the top left corner of the rectangle
  3. the width of the rectangle drawn to the right of the top left corner
  4. the height of the rectangle drawn from the top left corner downwards

With this code, your game should now look like the canvas below

Collision Detection and Game Over

Before we move the cat, we're going to enable collision detection so we know that the dog has caught the cat.

Since both the cat and dog are circles, we can get the distance between the centers of the circle. If the distance is equal to or smaller than the sum of the two radius, then we know that the dog has caught the cat.

The length of the line formula is as follows:

\text{Given the position of the cat as } (x_c, y_c)\\ \text{ and the position of the dog as } (x_d, y_d)\text{,}\\ \text{ then the distance }d \text{ between the cat and dog is defined as}\\ \text{ }\\ \Large{d=\sqrt{(x_c - x_d)^2 + (y_c - y_d)^2}}

In Javascript code, the above formula will look like:

var d = Math.sqrt((catX - dogX)*(catX - dogX) 
   + (catY - dogY)*(catY - dogY));

The square of a number is the number multiplied by itself.

Now that we know the distance, we have to check if the distance between the two centers are smaller than or equal to the sum of the radiuses. So we will use an if statement to check if the cat and dog hit each other. When they do, we'll set a boolean flag called stopGame to true.

We'll put the code in the update function accordingly.

function update()
{
// the distance between the cat and dog
  var d = Math.sqrt((catX - dogX)*(catX - dogX) 
     + (catY - dogY)*(catY - dogY));

// cat and dog have hit each other if they the distance is
// smaller than the sum of the radiuses
  if(d <= catSize + dogSize)
  {
    stopGame = true;
  }
}

We will declare and initialize the stopGame variable at the top of the script outside of any function.

// cat variables
var catX = 100; // the x position of the cat
var catY = 100; // the y position of the cat
var catSize = 20; // the radius of the cat is 20 pixels

// dog variables
var dogX = 700; // the x positon of the dog
var dogY = 500; // the y position of the dog
var dogSize = 20; // the radius of the dog is 20 pixels

// game variables
var stopGame = false; // game stops when this variable is true

// canvas properties
// identify the correct canvas
var canvas = document.getElementById("gameAreaDemo5"); 
var ctx = canvas.getContext("2d"); // get the 2D context

With all that set, we just need to modify the main function to stop the game when the dog catches the cat. To stop the game, the main function will only call itself when stopGame is false. When stopGame is true, we'll display an alert saying "The dog has caught the cat!".

Modify the main function with the new if-else statement as shown below.

function main()
{
  update();
  draw();
  
  // continue the game only if stopGame is false
  if(stopGame == false)
  {
    // call the main function again to create the loop
    window.requestAnimationFrame(main);
  }
  else // stopGame is true
  {
    // display alert box
    alert("The dog has caught the cat!");
  }
}

With all those modifications, your game should look like the following canvas below. When the dog collides with the cat, the alert box should appear and the game will stop.

Moving the Cat with the Keyboard

The last thing we need to do is to move the cat. We'll be using the keyboard to move the cat. The keyboard has several actions, but we're going to focus on two.

  1. keydown detects if the key on the keyboard is being pressed
  2. keyup detects when a pressed key is released

NOTE: The above to events do not refer to the up and down arrow keys on the keyboard!

What we're going to do with the cat is to set a speed for the cat to move when the key is pressed. Then we'll add the speed to the current position, so that the cat will actually move.

First, let's make the variables to keep track of the speed. We will set a horizontal speed to control movement on the x-axis and a vertical speed to control movement on the y-axis.

// cat variables
var catX = 100; // the x position of the cat
var catY = 100; // the y position of the cat
var catSize = 20; // the radius of the cat is 20 pixels
var catXSpeed = 0; // the horizontal speed of the cat
var catYSpeed = 0; // the vertical speed of the cat

Next, in the update function, we'll add the code to add the speed to the current position so the cat will move.

function update()
{
  // move the cat
  catX = catX + catXSpeed; // update horizontal position
  catY = catY + catYSpeed; // update vertical position

  // the distance between the cat and dog
  var d = Math.sqrt((catX - dogX)*(catX - dogX) 
     + (catY - dogY)*(catY - dogY));

  // cat and dog have hit each other if they the distance is
  // smaller than the sum of the radiuses
  if(d <= catSize + dogSize)
  {
    stopGame = true;
  }
}

If you run the game now, the cat still doesn't move as we haven't added an action listener to detect the pressing of a key. Let's do that now.

First, we'll add the listener to our program. Similar to the mousemove event listener, we add one for the keyboard keydown event. When a key is pressed, we will call the moveCat function.

// canvas properties
// identify the correct canvas
var canvas = document.getElementById("gameAreaDemo6"); 
var ctx = canvas.getContext("2d"); // get the 2D context

// capture mouse event
canvas.addEventListener("mousemove", moveDog);

// capture keyboard events
window.addEventListener("keydown", moveCat);

function moveDog(event)
{
  var canvasRect = canvas.getBoundingClientRect();
  dogX = event.clientX - canvasRect.left;
  dogY = event.clientY - canvasRect.top;
}

We will now create the moveCat function where we will check which key is pressed and move the cat accordingly. We will use the following keys to control the cat

  • w: move the cat up
  • s: move the cat down
  • a: move the cat left
  • d: move the cat right

event.key will tell us what key is pressed. Using an if statement, we can compare and see if it's one of the keys we want.

The moveCat function is as follows. You can put it before the moveDog function.

// capture mouse event
canvas.addEventListener("mousemove", moveDog);

// capture keyboard events
window.addEventListener("keydown", moveCat);

function moveCat(event)
{
  if(event.key == "d")
  {			
    catXSpeed = 10;
  }
  else if(event.key == "a")
  {
    catXSpeed = -10;
  }
  else if(event.key == "s")
  {
    catYSpeed = 10;
  }
  else if(event.key == "w")
  {
    catYSpeed = -10;
  }
}

function moveDog(event)
{
  var canvasRect = canvas.getBoundingClientRect();
  dogX = event.clientX - canvasRect.left;
  dogY = event.clientY - canvasRect.top;
}

Note that the key value is a string, so we use the double quotes. Also note that when we press "w" to go up, the catYSpeed is negative as y decreases as we move up the canvas.

If you save and run your game, you should get something like the canvas below.

As you have probably noticed, the cat does not stop moving when you stop pressing a key. That is because we did not implement an event listener to detect when we stop pressing a key. the action listener for that is keyup.

Let's add it into our program. We will call the function catStop when the key is released.

// capture mouse event
canvas.addEventListener("mousemove", moveDog);

// capture keyboard events
window.addEventListener("keydown", moveCat);
window.addEventListener("keyup", stopCat);

The stopCat function is basically the same as the moveCat function except we set the speed to 0. We set the speed to 0 because we want the cat to stop moving in the specified direction. Let's put it after the moveCat function and before the moveDog function.

function moveCat(event)
{
  if(event.key == "d")
  {			
    catXSpeed = 10;
  }
  else if(event.key == "a")
  {
    catXSpeed = -10;
  }
  else if(event.key == "s")
  {
    catYSpeed = 10;
  }
  else if(event.key == "w")
  {
    catYSpeed = -10;
  }
}

function stopCat(event)
{
  if(event.key == "d")
  {			
    catXSpeed = 0;
  }
  else if(event.key == "a")
  {
    catXSpeed = 0;
  }
  else if(event.key == "s")
  {
    catYSpeed = 0;
  }
  else if(event.key == "w")
  {
    catYSpeed = 0;
  }
}

function moveDog(event)
{
  var canvasRect = canvas.getBoundingClientRect();
  dogX = event.clientX - canvasRect.left;
  dogY = event.clientY - canvasRect.top;
}

With that code added, your game should now be fully working. It should look like canvas below.

Complete Source Code

<!DOCTYPE html>
<html>
<head>
<title>Cat and Dog</title>
<style>
#gameArea{
  border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="gameArea" width="800" height="600"></canvas>
<script>
// cat variables
var catX = 100; // the x position of the cat
var catY = 100; // the y position of the cat
var catSize = 20; // the radius of the cat is 20 pixels
var catXSpeed = 0; // the horizontal speed of the cat
var catYSpeed = 0; // the vertical speed of the cat

// dog variables
var dogX = 700; // the x positon of the dog
var dogY = 500; // the y position of the dog
var dogSize = 20; // the radius of the dog is 20 pixels

// game variables
var stopGame = false; // game stops when this variable is true

// canvas properties
// identify the correct canvas
var canvas = document.getElementById("gameArea); 
var ctx = canvas.getContext("2d"); // get the 2D context

// capture mouse event
canvas.addEventListener("mousemove", moveDog);

// capture keyboard events
window.addEventListener("keydown", moveCat);
window.addEventListener("keyup", stopCat);

function moveCat(event)
{
  if(event.key =="d")
  {			
    catXSpeed = 10;
  }
  else if(event.key == "a")
  {
    catXSpeed = -10;
  }
  else if(event.key == "s")
  {
    catYSpeed = 10;
  }
  else if(event.key == "w")
  {
    catYSpeed = -10;
  }
}

function stopCat(event)
{
  if(event.key == "d")
  {			
    catXSpeed = 0;
  }
  else if(event.key == "a")
  {
    catXSpeed = 0;
  }
  else if(event.key == "s")
  {
    catYSpeed = 0;
  }
  else if(event.key == "w")
  {
    catYSpeed = 0;
  }
}


function moveDog(event)
{
  var canvasRect = canvas.getBoundingClientRect();
  dogX = event.clientX - canvasRect.left;
  dogY = event.clientY - canvasRect.top;
}

function update()
{
  // move the cat
  catX = catX + catXSpeed; // update horizontal position
  catY = catY + catYSpeed; // update vertical position

  // the distance between the cat and dog
  var d = Math.sqrt((catX - dogX)*(catX - dogX) 
     + (catY - dogY)*(catY - dogY));

// cat and dog have hit each other if they the distance is
// smaller than the sum of the radiuses
  if(d <= catSize + dogSize)
  {
    stopGame = true;
  }
}

function draw()
{
  // clear canvas by painting the whole canvas white
  ctx.fillStyle="white";
  ctx.fillRect(0,0,800,600);

  // draw cat
  ctx.fillStyle="#0000FF"; // cat will be blue (hexadecima colors)
  ctx.beginPath(); // start a new path
  ctx.arc(catX, catY, catSize, 0, 2*Math.PI); // specify the arc
  ctx.fill(); // fill the area the circle encloses with blue

  // draw dog
  ctx.fillStyle="#00FF00"; // dog will be green (hexadecimal colors)
  ctx.beginPath(); // start a new path
  ctx.arc(dogX, dogY, dogSize, 0, 2*Math.PI); // specify the arc
  ctx.fill(); // fill the area the circle encloses with green
}

function main()
{
  update();
  draw();
  
  // continue the game only if stopGame is false
  if(stopGame == false)
  {
    // call the main function again to create the loop
    window.requestAnimationFrame(main);
  }
  else // stopGame is true
  {
    // display alert box
    alert("The dog has caught the cat!");
  }
}

// call the main function to start the game
window.requestAnimationFrame(main);
</script>
</body>
</html>

Adding New Features

This is a fairly simple game to introduce the update draw loop of a typical computer game. Feel free to build and expand on this game to add other mechanics.

Some features you may want to try to make are:

  • keeping the cat on the screen
  • being able to restart the game
  • powerups to change the size of the cat or dog
  • number of lives
  • a timer
  • change the circles to images

The features can go on and on. Use your imagination and have fun!

2 Replies to “A Basic Game In Javascript: Cat and Dog”

    1. Hi Jetski101,

      If you want to change the game so the cat chases the dog, you just have to change the text in the alert message when they collide. Since the game just detects whether the two objects collide, to the computer, it doesn’t matter who hit who. You, as the developer, need to give that event the context in your game.

      To be able to do animation, you first need to add images to your game.
      You can refer to the mozilla documetation here: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage

      The easiest way to add animation is to put all your frames into an array and draw a different frame each time the character is drawn.

      You can get sprite animations to popular videogame characters at https://www.spriters-resource.com/
      They’re usually all in one image, but you can split each frame to a separate image.

      If you want to use the same image, you can use one of the drawImage functions documented in the Mozilla page to draw the desired part of the image for each frame.

      Hope that helps.
      Jeffrey

Leave a Reply to Jeffrey Wong Cancel reply

Your email address will not be published. Required fields are marked *