Level-Up Coding

Level 4: You can animate things by redrawing the canvas.

HTML Canvas can draw pictures, but to make a game you have to move things on the screen. Most video games do this by redrawing the whole canvas dozens of times per second so that it looks like things are moving smoothly. Javascript has a built-in way to tell the computer to redraw the screen over and over again.


Variables and types

In a previous lesson you learned that variables are the way that computer programs keep track of things. You also learned that each piece of data has a type, like a number, string, or boolean.

In some programming languages, variables can only hold specific types of data. These are called strongly typed languages. For example, in C++ some variables can only store integers. If you want a string, you have to get a different variable.

Javascript is called a dynamically typed langauge. A variable in Javascript can hold any type of data, and this can change over time. So, a variable might start out storing an number, and then later store a Boolean, and still later store a string.


Object variables

Sometimes it is useful to group several data items in one variable. For example, suppose you are writing a program to keep track of students and you want to store the first name, last name, and grade for each student. To handle this, Javascript allows you to define object type variables.

Remember that we said a variable is like a bin in the computer where you can store things. The label on the bin is the variable’s name. An object is like a big bin that can hold other bins inside it, each with a label on it. These internal bins or variables are called the properties of the object. Sometimes we also call them key-value pairs.

To create a new object, you use curly braces {}. Then, inside the curly braces, add all of the properties that you want for the object. For each property, you give the property name, followed by a colon :, followed by the property value. The example below creates a student object with a firstName, lastName, and grade properties:

    var student = 
    {
        firstName: "Grace",
        lastName: "Hopper",
        grade: "A"
    };

To get at one of the properties in an object, we use the dot operator. For example, this code prints the firstName property of student to the console:

    console.log( student.firstName );

You can add a new property to the object using the dot operator. This code adds an age to the student:

    student.age = 99;

Camel Case

You may have noticed that firstName is written in kind of a funny way. There are no spaces between the words, but the first letter of each word except the first is capitalized. This is called camelCase because it looks kind of like a camel’s back. Usually, you don’t want spaces in the name of a variable, and in fact most computer languages do not allow it, so we use camelCase instead.


The gameState object variable

The example code for this level defines a variable called gameState. This variable will store all the information about the objects on the screen. We declare the variable at the top of the script with the code:

    var gameState;

Declaring a variable tells the computer that you will be using it, and also sets the variable’s scope, which is the set of locations where the variable can be used. By declaring gameState outside of any function, it can be used anywhere in the program. This is sometimes called the global scope. When you define a variable in a function, it has a local scope. It can only be used in that function.


Initializing the gameState

gameState was declared at the top of the script, but we initialize it in the function window.onload. You initialize a variable when you set its value for the first time. Here is the code to initialize gameState:

   gameState = {};
   gameState.ship = 
   {
       x: 0,
       y: 100,
       image: document.getElementById("shipImage")
   };

The first line gameState = {}; tells the computer to make a new object with nothing inside it and assign it to gameState.

The next lines define the ship property of the gameState, which is itself an object variable. You can tell this because of the curly braces {}.

As part of the ship, we define three properties: x, y, and image. These will define the position of the ship on the screen, and the image that will be displayed for the ship.

The next line document.getElementById("shipImage"); is pretty long, but you have seen code like it before. Remember that this code gets a reference to the shipImage element on the page.


Drawing the ship

Programs can get complicated, and it’s a good idea to try and organize your code so that things make sense. One way to do this is to use different functions to do different things. In the example for this level, the code to draw the canvas is put in a function called drawGame. It works by clearing the canvas in a light blue color and drawing the ship:

var drawGame = function()
{
    var w = canvas.width;
    var h = canvas.height;

    // Draw a background rectangle
    drawing.fillStyle = "#BBBBFF";  // light blue fill color
    drawing.fillRect(0,0,w,h);      // fill the canvas with light blue
    drawing.strokeRect(0,0,w,h);    // draw a black border

    // Draw the ship image
    var s = gameState.ship;    // get a reference to the ship
    drawing.drawImage(s.image, s.x, s.y);
}

Object references

One thing to notice about the code above is the part that draws the image. The line var s = gameState.ship; makes a variable called s, but it does not actually make a copy of the ship. Instead, s is just a reference to the same value as gameState.ship, sort of like putting two labels on the same bin in the computer. Sometimes, we say that the s and gameState.ship “point to” the same value. Why would we want to do this? To make the code simpler. If we did not define s, the line to draw the image would have to be:

drawing.drawImage(gameState.ship.image, gameState.ship.x, gameState.ship.y);

window.requestAnimationFrame

drawGame draws the canvas one time. To do animation, you need to redraw the canvas over and over, changing something each time. In fact, to make the motion look smooth, you have to redraw it 30 or 60 times per second!

In Javascript, we can make the browser run a function over and over using the function window.requestAnimationFrame. In the code for this level, we put the call in a function called animateGame:

var animateGame = function()
{
    // Call animateGame again in about 1/60 of a second
    window.requestAnimationFrame(animateGame);
    ....

window.requestAnimationFrame(animateGame) tells the computer to call animateGame again in about 1/60 of a second. Then, in about 1/60 of a second, the code runs again. This again tells the computer to call animateGame, and so on.


The animateGame function

The animateGame function has two major jobs. These are

  1. Update everything about the game for one “frame” of time (about 1/60 second.)
  2. Redraw the game based on how things updated.

In the example code, the only thing that changes each time is the x position of the ship. This causes the ship to move horizontally across the screen:

    // change the x location of the ship
    var s = gameState.ship;
    s.x += 1.0;
    if (s.x > canvas.width)
    {
        s.x = -s.image.width;
    }

The line s.x += 1.0; adds 1 to the ship’s x coordinate at each time step. This causes it to move from left to right across the canvas.

the second part of the code tests to see if the ship has moved off of the canvas to the right using an if statement. If the ship has moved off to the right, the code resets the position of the ship to be to the left of the canvas (s.x = -s.image.width;).

Here’s some things to think about:

  1. How would you make the ship move left instead of right?
  2. How would you make the ship move up or down?
  3. How could you make the ship bounce off the edge of the canvas instead of wrapping around?

Putting it All Together

Putting everything together from this level gives the code below.

<!DOCTYPE html>
<html>
<head>
    <style>
    body { background-color: black; }
    h1 { color: #FFFF00; }
    </style>
    <title>Moving Ship</title>
</head>

<body>
    <center>
        <h1>Moving Ship</h1>
        <canvas id="myCanvas" width="600" height="400"></canvas>
    </center>

    <img id="shipImage" style="display:none;" src="shipImage.png"></img>

    <script>
    //-------------------------------------------------------------------
    // The gameState variable will store all information about the
    // game, such as what objects exist, where they are and the score.
    // Since these variables are defined in the global scope, they
    // can be used anywhere in the program.
    //-------------------------------------------------------------------

    var canvas;
    var drawing;
    var gameState;

    //-------------------------------------------------------------------
    // drawGame redraws the whole canvas for each frame of the game.
    //-------------------------------------------------------------------

    var drawGame = function()
    {
        var w = canvas.width;
        var h = canvas.height;

        // Draw a background rectangle
        drawing.fillStyle = "#BBBBFF";  // light blue fill color
        drawing.fillRect(0,0,w,h);      // fill the canvas with light blue
        drawing.strokeRect(0,0,w,h);    // draw a black border

        // Draw the ship image
        var s = gameState.ship;   // get a reference to the ship 
        drawing.drawImage(s.image, s.x, s.y);
    }

    //-------------------------------------------------------------------
    // The animate function gets called for each frame. It updates
    // the gameState variable and then calls drawGame.
    //-------------------------------------------------------------------

    var animateGame = function()
    {
        // Call animateGame again in about 1/60 of a second
        window.requestAnimationFrame(animateGame);

        // change the x location of the ship
        var s = gameState.ship;
        s.x += 1.0;
        if (s.x > canvas.width)
        {
            s.x = -s.image.width;
        }

        // Draw the game
        drawGame();
    }

    //-------------------------------------------------------------------
    // The window.onload function will run when the page first loads
    //-------------------------------------------------------------------

    window.onload = function() 
    {
        // Get the canvas and drawing context
        canvas = document.getElementById("myCanvas");
        drawing = canvas.getContext("2d");

        // Initialize the gameState with a variable for the ship
        gameState = {};
        gameState.ship = 
        {
            x: 0,
            y: 100,
            image: document.getElementById("shipImage")
        };

        // Call animate for the first time, starting the game loop
        animateGame();
    }
    </script> 
</body>
</html>

Terms


Exercise

  1. Copy the example code for the moving ship and save it as an HTML page in its own folder.

  2. Copy the shipImage.png and robotImage.png files to the same folder where you saved the HTML page. You can right click on them and select the “save image as” option to do this.

  3. Change how the ship moves. Try some of the following:

    • Make the ship start at a different position.
    • Make it go a different direction or speed.
    • Try to make it wrap around in other directions besides on the right edge.
  4. Add a copy of the robot image and and animate it. To do this, you should do the following:

    • Make an <img> tag to hold the robot in the HTML part of the file.
    • Add new variables for the robot to the gameState variable in window.onLoad.
    • Add code to the drawGame function to draw the robot.
    • Add code to the animateGame function to move the robot.

Bonus Level

  1. Make the robot bounce off the walls so he goes back and forth or up and down.