Using HTML5 canvas and JavaScript to move a vehicle in a game

This topic explains how to use HTML5 canvas and JavaScript to move the green and orange spaceship that you created in the Drawing the background and your vehicle topic.

  • Canvas Code Sample
  • Canvas Code Sample Discussion
    • Body Code
    • Script Code
  • Canvas Animation
  • Related topics

This topic includes a stand-alone annotated code sample written in HTML5 and JavaScript that shows you how to move the spaceship across the star field. Canvas uses immediate mode to create this moving image. Two steps are required to implement this feature in the game program. First, you must redraw the image every time you move it. Second, you must restore the background that was destroyed when you drew the spaceship over it. This code sample shows you how to erase the image of the spaceship as it moves and draw a new ship in the new position. Then it explains how to save and restore snapshots of the ship and the background. Canvas does not keep track of where the pixels are drawn, so the sample shows you how to keep track of every pixel as it is saved, erased, moved, and restored in your code.

This code sample covers the following tasks that demonstrate the basic principles of using Canvas to move an object across a detailed background:

  • Setting up a game loop to process main game events
  • Capturing keystrokes to determine where the ship should move next
  • Saving the background where the ship will be moving to
  • Moving the ship to its new position
  • Restoring the old background that the ship was covering up

At the end of the code sample is discussion material that explains more about the design and structure of these tasks and how they work.

Canvas Code Sample

<!DOCTYPE html>
<html>
  
  <head>
    <script type="text/javascript">
      // Global variables
      var shipX = 0; // X position of ship
      var shipY = 0; // Y position of ship
      var canvas; // canvas
      var ctx; // context
      var back = new Image(); // storage for new background piece
      var oldBack = new Image(); // storage for old background piece
      var ship = new Image(); // ship
      var shipX = 0; // current ship position X
      var shipY = 0; // current ship position Y
      var oldShipX = 0; // old ship position Y
      var oldShipY = 0; // old ship position Y
      // This function is called on page load.


      function canvasSpaceGame() {

        // Get the canvas element.
        canvas = document.getElementById("myCanvas");

        // Make sure you got it.
        if (canvas.getContext)

        // If you have it, create a canvas user interface element.
        {
          // Specify 2d canvas type.
          ctx = canvas.getContext("2d");

          // Paint it black.
          ctx.fillStyle = "black";
          ctx.rect(0, 0, 300, 300);
          ctx.fill();

          // Save the initial background.
          back = ctx.getImageData(0, 0, 30, 30);

          // Paint the starfield.
          stars();

          // Draw space ship.
          makeShip();
        }

        // Play the game until the until the game is over.
        gameLoop = setInterval(doGameLoop, 16);

        // Add keyboard listener.
        window.addEventListener('keydown', whatKey, true);

      }

      // Paint a random starfield.


      function stars() {

        // Draw 50 stars.
        for (i = 0; i <= 50; i++) {
          // Get random positions for stars.
          var x = Math.floor(Math.random() * 299);
          var y = Math.floor(Math.random() * 299);

          // Make the stars white
          ctx.fillStyle = "white";

          // Give the ship some room by painting black stars.
          if (x < 30 || y < 30) ctx.fillStyle = "black";

          // Draw an individual star.
          ctx.beginPath();
          ctx.arc(x, y, 3, 0, Math.PI * 2, true);
          ctx.closePath();
          ctx.fill();

          // Save black background.
          oldBack = ctx.getImageData(0, 0, 30, 30);
        }
      }

      function makeShip() {

        // Draw saucer bottom.
        ctx.beginPath();
        ctx.moveTo(28.4, 16.9);
        ctx.bezierCurveTo(28.4, 19.7, 22.9, 22.0, 16.0, 22.0);
        ctx.bezierCurveTo(9.1, 22.0, 3.6, 19.7, 3.6, 16.9);
        ctx.bezierCurveTo(3.6, 14.1, 9.1, 11.8, 16.0, 11.8);
        ctx.bezierCurveTo(22.9, 11.8, 28.4, 14.1, 28.4, 16.9);
        ctx.closePath();
        ctx.fillStyle = "rgb(222, 103, 0)";
        ctx.fill();

        // Draw saucer top.
        ctx.beginPath();
        ctx.moveTo(22.3, 12.0);
        ctx.bezierCurveTo(22.3, 13.3, 19.4, 14.3, 15.9, 14.3);
        ctx.bezierCurveTo(12.4, 14.3, 9.6, 13.3, 9.6, 12.0);
        ctx.bezierCurveTo(9.6, 10.8, 12.4, 9.7, 15.9, 9.7);
        ctx.bezierCurveTo(19.4, 9.7, 22.3, 10.8, 22.3, 12.0);
        ctx.closePath();
        ctx.fillStyle = "rgb(51, 190, 0)";
        ctx.fill();

        // Save ship data.
        ship = ctx.getImageData(0, 0, 30, 30);

        // Erase it for now.
        ctx.putImageData(oldBack, 0, 0);

      }

      function doGameLoop() {

        // Put old background down to erase shipe.
        ctx.putImageData(oldBack, oldShipX, oldShipY);

        // Put ship in new position.
        ctx.putImageData(ship, shipX, shipY);

      }

      // Get key press.


      function whatKey(evt) {

        // Flag to put variables back if we hit an edge of the board.
        var flag = 0;

        // Get where the ship was before key process.
        oldShipX = shipX;
        oldShipY = shipY;
        oldBack = back;

        switch (evt.keyCode) {

          // Left arrow.
        case 37:
          shipX = shipX - 30;
          if (shipX < 0) {
            // If at edge, reset ship position and set flag.
            shipX = 0;
            flag = 1;
          }
          break;

          // Right arrow.
        case 39:
          shipX = shipX + 30;
          if (shipX > 270) {
            // If at edge, reset ship position and set flag.
            shipX = 270;
            flag = 1;
          }
          break;

          // Down arrow
        case 40:
          shipY = shipY + 30;
          if (shipY > 270) {
            // If at edge, reset ship position and set flag.
            shipY = 270;
            flag = 1;
          }
          break;

          // Up arrow 
        case 38:
          shipY = shipY - 30;
          if (shipY < 0) {
            // If at edge, reset ship position and set flag.
            shipY = 0;
            flag = 1;
          }
          break;

        }

        // If flag is set, the ship did not move.
        // Put everything back the way it was.
        if (flag) {
          shipX = oldShipX;
          shipY = oldShipY;
          back = oldBack;
        } else {
          // Otherwise, get background where the ship will go
          // So you can redraw background when the ship
          // moves again.
          back = ctx.getImageData(shipX, shipY, 30, 30);
        }
      }
    </script>
  </head>
  
  <body onload="canvasSpaceGame()">
    <h1>
      Canvas Space Game
    </h1>
    <canvas id="myCanvas" width="300" height="300">
    </canvas>
  </body>

</html>

Canvas Code Sample Discussion

Note  The code in this sample builds upon the code in the previous sample.

 

This section explains the design and structure of the code sample. It will tell you about the different parts and how they fit together. The Canvas sample uses a standard HTML5 header, <!doctype html>, so that browsers can distinguish it as part of the HTML5 specification.

This code is divided into two major parts:

  • Body Code
  • Script Code

Body Code

The body code is the same as the body code in the first task of this scenario, "Drawing the background and your vehicle."

Script Code

The script code contains several blocks of code fromthe first task of this scenario, "Drawing the background and your vehicle." However, the code modifies every block and adds several new ones. The script code of this sample consists of the following:

  • Global variables
  • canvasSpaceGame function
  • stars function
  • makeShip function
  • doGameLoop function
  • whatKey function

Global variables are called when the page loads. The canvasSpaceGame function is called from the body tag onload attribute. The rest of the functions are called from the canvasSpaceGame function.

Global Variables

This program uses global variable for data that will be accessed from different functions. The variables include those in the following list:

  • canvas and context - you might want to make these global for most Canvas programs because you will be accessing them from many functions.
  • back, oldBack – images that store the new and old background 30x30 pixel snapshots.
  • ship – image that stores the pixels for the ship vehicle.
  • shipX, shipY – the x and y coordinates for the ship vehicle.
  • oldShipX, oldShipY – the x and y coordinates of where the ship vehicle was in its last position.

canvasSpaceGame Function

This function is based on the same function in the first task of this scenario, "Drawing the background and your vehicle." The following modifications have been made:

  • A 30x30 pixel snapshot is created by using the getImageData method and saving it to the back variable. This variable is used later when the ship is moved.
  • A call to setInterval, which runs the doGameLoop function every 16 milliseconds. This interval will be cancelled at the end of the game using gameLoop.
  • An event handler is set up using addEventListener. This calls the whatKey function every time the keydown event takes place.

stars Function

This is the same as the stars function in the first task of "Drawing the background and your vehicle." The only difference is that that a 30x30 pixel snapshot of the background is saved to the oldBack variable image for later use.

makeShip Function

This is the same as the makeShip function in the first task of this scenario, "Drawing the background and your vehicle." The only difference is that a 30x30 pixel snapshot of the ship is saved to the ship image for later use.

doGameLoop Function

This function is called every 16 milliseconds by the gameLoop timer. Game loops are very common in games because they perform basic animation tasks at regular intervals.

The game loop calls two animation tasks:

  • Erasing the ship - If the ship moves and leaves a copy of the ship on the background, you must restore the old background image. This uses the putImageData method. If you do not do this, every time you move the ship, the old image of the ship remains. You must know what was under the ship before it moved and copy that image to erase the old ship image and replace it with the background image that was there before.
  • Moving the ship – You must copy the ship image to the new ship position. Because this will destroy whatever image was there before, you must save the background before you destroy it.

whatKey Function

This function is called every time a key is pressed down. It determines where the player wants the ship to move. It is important to note that the ship does not actually move in this function. The move is calculated, but the drawing of the new ship takes place in the doGameLoop function.

The whatKey function contains the following actions:

  • A flag will be used to determine what happens if the ship reaches the edge of the screen.
  • Before the ship can move, you must save the x and y positions of the ship and save the background that is under the ship. This is required because immediate mode does not automatically save any pixels on the screen.
  • Then a switch statement processes the event and looks for the event key code. The key codes correspond to the left, right, up, and down keys on your keyboard.
  • For each relevant case of the key code, the new x and y positions are calculated. If the new ship position would move the ship outside of the canvas, the new position is canceled and the flag is set.
  • If the flag is set, the ship and background variables are returned to their original state.
  • If the flag is not set, the getImageData method is used to save the current background snapshot.

Canvas Animation

Canvas has two ways it can create animations. The first is the most common form of animation where the screen is completely redrawn for each motion. This works better if you have a small canvas drawing surface and a fast computer. But it is not recommended for larger or more complex animation. The second way Canvas creates animations is demonstrated in the code sample included in this topic. This method is recommended for when you have a larger and more complex Canvas animation. This style of animation is more advanced. Even though it takes more code to set it up, it runs much faster than the more common style of animation. It runs faster because you only change a few individual pixels with each motion, not the entire screen of pixels. The faster style of animation shown in this sample uses the following steps. These steps are repeated in a cycle for each motion.

  • Erase the ship by drawing over it with a 30x30 pixel snapshot of the old background.
  • Get a new snapshot of the background that will be covered up when the ship moves.
  • Draw the ship in its new position.

Because this circular loop happens every 16 milliseconds, the human eye does not see that the ship is erased, it only sees the ship as moving. This reduces the risk of screen blinking between each move, because only a small part of the screen is drawn each time.

How to Use Canvas to Create a Space Game