Showing posts with label collision detection. Show all posts
Showing posts with label collision detection. Show all posts

Monday, February 24, 2014

Flying Fox Part 3 (Game Programming)

Today is the deadline for the Flappy Jam (read all about it here: http://firefoxosgaming.blogspot.com/2014/02/game-jams-game-programming.html. I definitely didn't write this series of posts on Flying Fox just to enter the jam, but I admit that I was interested in how I could make a simple version of Flappy Bird. The main interest was that I wanted to try a one-finger one-meaning game. The earlier two posts in this series were:

Making the Fox Fly
http://firefoxosgaming.blogspot.com/2014/02/flying-fox-part-1-game-programming.html

Making the Clouds Move
http://firefoxosgaming.blogspot.com/2014/02/svg-and-z-order-game-programming.html

The first post showed how to import a complex SVG drawing made in Inkscape and make it jump up if it is touched and to fall down if it is not. The second post talked about z-order in SVG (there isn't any) and showed how to make clouds fly by, creating the illusion that the Fox is flying.

Today's post wraps up things. Three final parts are added:

1. Pipes are moving and the Fox must avoid them.

2. A way to detect if the Fox collides with the pipe.

3. The result of any Fox/Pipe collision.

Here's what it will look like when the Fox is flying:


Here's the Fox about to hit a pipe:


And here is the exploded Fox after he hits the pipe a second later:


And so, here is the code, all 408 lines. (Not too much, really, and I hope it is well-enough organized to follow along. A lot of this code is reused from other posts.)

<!DOCTYPE HTML>
<html>
<head>
  <meta charset="utf-8">
  <title>
    Floppy Fox Part 3
  </title>
 
  <style>
        
    #mySVG
    {
      width: 360px;
      height: 620px;
      position: absolute;
      top: 0px;
      left: 0px;
    }
      
  </style>

  <script>

  // --- Global variables ---

  // Width and height of board in pixels
  var boardWidth = 360;
  var boardHeight = 620;
  var boardWidthPixels = boardWidth + "px";
  var boardHeightPixels = boardHeight + "px";

  // URL for W3C definition of SVG elements
  var svgURL =
    "http://www.w3.org/2000/svg";
  var svgLink =
    "http://www.w3.org/1999/xlink";

  // Variables for fox initial position.
  var foxX = boardWidth / 2 - 100;
  var foxY = boardHeight / 2;
  var foxH = 50;
  var foxW = 50;
 
  // Cloud initial x coordinate value.
  var cloud1X = boardWidth;
  var cloud2X = boardWidth / 2;
 
  // Pipe globals
  var uPipeLength = 260;
  var lPipeLength = 260;
  var pipeWidth = 50;
  var uPipeX = boardWidth - pipeWidth;


  // Use requestAnimationFrame.
  var requestAnimationFrame =
  window.requestAnimationFrame ||
  window.mozRequestAnimationFrame ||
  window.webkitRequestAnimationFrame ||
  window.msRequestAnimationFrame;

  // --- Event listeners ---

  // Page load event listener.
  window.addEventListener("load",
    runFirst, false);

  // Runs when page loads.
  function runFirst() {

    // Define the game board as SVG element.
    gameBoard =
      document.
        createElementNS(svgURL, "svg");

    // Width and height of SVG board element.
    gameBoard.width.baseVal.valueAsString =
      boardWidthPixels;
    gameBoard.height.baseVal.valueAsString =
      boardHeightPixels;
    gameBoard.id = "mySVG";
   
    // You must append the board to the body.
    document.getElementById("pageBody").
      appendChild(gameBoard);

    // Add a background element for color.
    gameBack =
      document.
        createElementNS(svgURL, "rect");
    gameBack.x.baseVal.valueAsString =
      "0px";
    gameBack.y.baseVal.valueAsString =
      "0px";
    gameBack.width.baseVal.valueAsString =
      boardWidthPixels;
    gameBack.height.baseVal.valueAsString =
      boardHeightPixels;       
    gameBack.style.
          setProperty("fill","Thistle","");
         
    // Attach the background to  game board.
    gameBoard.appendChild(gameBack);
   
    // Create cloud1.
    cloud1 =
      document.
        createElementNS(svgURL, "circle");
    cloud1.cx.baseVal.valueAsString =
      cloud1X + "px";
    cloud1.cy.baseVal.valueAsString =
      "400px";
    cloud1.r.baseVal.valueAsString =
      "50px";       
    cloud1.style.
          setProperty("fill","blue","");
         
    // Attach cloud1 to the game board.
    gameBoard.appendChild(cloud1);
   
    // Create cloud2.
    cloud2 =
      document.
        createElementNS(svgURL, "circle");
    cloud2.cx.baseVal.valueAsString =
      cloud2X + "px";
    cloud2.cy.baseVal.valueAsString =
      "200px";
    cloud2.r.baseVal.valueAsString =
      "50px";       
    cloud2.style.
          setProperty("fill","green","");
         
    // Attach cloud1 to the game board.
    gameBoard.appendChild(cloud2);
  
    // Mouse down event listener
    gameBoard.addEventListener(
      "mousedown", foxJump, false);
       
    // Import SVG fox image.
    // Set height, width, x, and y.
    myFox = document.
      createElementNS(svgURL,"image");
    myFox.setAttribute("height",foxH);
    myFox.setAttribute("width",foxW);
    myFox.setAttribute("x",foxX);
    myFox.setAttribute("y",foxY);
   
    // Use different namespace for xlink.
    myFox.setAttributeNS(svgLink,
      "href","fox.svg");
   
    // Add the fox to the game board.
    gameBoard.appendChild(myFox);
   
    // Pipes
    // Upper Pipe
    // Define the pipe.
    uPipe = document.
      createElementNS(svgURL,"rect");
    // Set the values.
    uPipe.x.baseVal.valueAsString =
      uPipeX + "px";
    uPipe.y.baseVal.valueAsString = "0px";
    uPipe.width.baseVal.valueAsString =
      pipeWidth + "px";
    uPipe.height.baseVal.valueAsString =
      uPipeLength + "px";
    uPipe.style.setProperty("fill","red","");
    // Attach it to the board.
    gameBoard.appendChild(uPipe);

    // Lower Pipe
    // Define the pipe.
    lPipe = document.
      createElementNS(svgURL,"rect");
    // Set the values.
    lPipe.x.baseVal.valueAsString =
      uPipeX + "px";
    lPipe.y.baseVal.valueAsString =
      uPipeLength + 100 + "px";
    lPipe.width.baseVal.valueAsString =
      pipeWidth + "px";
    lPipe.height.baseVal.valueAsString =
      uPipeLength + "px";
    lPipe.style.setProperty("fill","red","");
    // Attach it to the board.
    gameBoard.appendChild(lPipe);
  
    // Start the game loop.
    gameLoop();
  }

  // Game loop.
  function gameLoop(){

    // Endless loop requestAnimationFrame.
    requestAnimationFrame(gameLoop);

    // The fox waits to drop.
    // Only you can save him.
    foxWait();
   
    // Move the clouds.
    moveClouds();
   
    // Move the pipes.
    movePipes();
  }
 
  // Game over!
  function gameOver() {

    // Make the fox big!
    myFox.setAttribute("height",200);
    myFox.setAttribute("width",200);
       
    //Wait wait a short time!
    foxBlowUpTimer =
      setTimeout(foxBlowUp, 700);
  }
 
  // Fox blow up!
  function foxBlowUp() { 
   
    // Reset fox values.
    foxX = boardWidth / 2 - 100;
    foxY = boardHeight / 2;
    foxW = 50;
    foxH = 50;
   
    // Reset fox position.
    myFox.setAttribute("height",foxH);
    myFox.setAttribute("width",foxW);
    myFox.setAttribute("x",foxX);
    myFox.setAttribute("y",foxY); 
  }
 
  // Move the pipes.
  function movePipes() {

    // Move the pipe X value. 
    uPipeX = uPipeX - 2;
   
    // Check if pipes off left edge.
    if (uPipeX < -50) {
   
      // If too far, send to right side.
      uPipeX = boardWidth;
     
      // Resize pipes.
      // Generate random change.
      change =
        Math.
          floor((Math.random()*60)+1) - 30;
       
     
      // Calculate new pipe lengths.
      uPipeLength = uPipeLength + change;
      lPipeLength = lPipeLength - change;
   
      // If upper pipe is too short, back up.
      if (uPipeLength < 100) {
        uPipeLength = uPipeLength - change;
        lPipeLength = lPipeLength + change;
        console.log("UPPER PIPE TOO SHORT");
      }   
     
      // If lower pipe is too short, back up.   
      if ((uPipeLength + 100) >
          (boardHeight)) {
        uPipeLength = uPipeLength - change;
        lPipeLength = lPipeLength + change;
        console.log("LOWER PIPE TOO SHORT");
      }
           
      // Draw upper pipe if new size.
      uPipe.height.baseVal.valueAsString =
        uPipeLength  + "px";
     
      // Draw lower pipe if new size.     
      lPipe.y.baseVal.valueAsString =
        uPipeLength + 150 + "px";
      lPipe.height.baseVal.valueAsString =
        lPipeLength + "px";
      
      // Display current values.      
      console.log(uPipeLength,
        lPipeLength, change);
    }
   
    // Test for upper pipe collision.
    if ((foxX > uPipeX) &&
        (foxX < (uPipeX + 50)) &&
        (foxY < uPipeLength)
        ){
     
      console.log("Hit upper pipe. Ouch!");
     
      // Game is over.
      gameOver();
    }

    // Test for lower pipe collision.
    if ((foxX > uPipeX) &&
        (foxX < (uPipeX + 50)) &&
        (foxY > (boardHeight - lPipeLength))
        ){
     
      console.log("Hit lower pipe. Ouch!");

      // Game is over.
      gameOver();
    }
   
    // Draw pipes at new x location.
    uPipe.x.baseVal.valueAsString = uPipeX;
    lPipe.x.baseVal.valueAsString = uPipeX;
  }
 
  // Move the clouds.
  function moveClouds() {
   
    // Move cloud 1.
    cloud1X = cloud1X - 0.5;
   
    // Does it go off the edge?
    if (cloud1X < -100) {
      cloud1X = boardWidth;
    }
   
    // Move cloud 2.
    cloud2X = cloud2X - 1;
   
    // Does it go off the edge?
    if (cloud2X < -100) {
      cloud2X = boardWidth;
    }

    cloud1.cx.baseVal.valueAsString =
      cloud1X + "px";
    cloud2.cx.baseVal.valueAsString =
      cloud2X + "px";     
  }

  // Fox is waiting to fall.
  function foxWait() {
 
    // Make the fox fall after one second.
    foxFallTimer = setTimeout(foxFall, 1000);
  }
 
  // Fox is falling.
  function foxFall() {
 
    // Make the fox fall one pixel.
    foxY = foxY + 1;
   
    // Does he hit the floor?
    if (foxY > (boardHeight - 50)){
     
      // Calculate new position.
      foxY = (boardHeight / 2) - 50; 

      // Game is over!
      gameOver();
      console.log("Fox hit the floor!");
    }

    // Draw the fox.
    myFox.y.baseVal.valueAsString =
      foxY + "px";
     
    // Report progress.
    console.log("Fox is falling.");
  }
 
  // Fox is jumping.
  function foxJump() {
 
    // Fox jumps up!
    foxY = foxY - 20;
   
    // Does he hit the roof?
    if (foxY < 20) {
     
      // Calculate new position.
      foxY = (boardHeight /2) - 50; 
     
      // Game is over!
      gameOver();
      console.log("Fox hit the roof!");
    }
   
    // Otherwise, draw the fox.
    myFox.y.baseVal.valueAsString =
      foxY + "px";
   
    // Report progress.
    console.log("Fox is jumping.");  
  }

  </script>
</head>
<body id="pageBody">
</body>
</html>


Most of this code is explained in the previous two posts, but here is the new stuff:

Globals

 I added a few globals to keep track of the pipe data. There are two pipes. They move from right to left (but at a different speed from the clouds).

  // Pipe globals
  var uPipeLength = 260;
  var lPipeLength = 260;
  var pipeWidth = 50;
  var uPipeX = boardWidth - pipeWidth;


The upper and lower pipes are given a length. This length will change to simulate new pipes that are of different lengths. This change happens off-screen, every time the pipes are hidden. When they come back on the right, their lengths will be changed, but the gap will be the same so the Fox has a fighting chance to get through.

The width of the pipe is a constant: 50 pixels.

I needed a value that would reflect the upper pipe's x value at the bottom point of the pipe. This will be used to calculate whether the bird hits either pipe. Essentially I needed a base point to measure from, and this seemed handy.

Adding the Pipes

I added two pipes, using SVG rectangles. Notice that I had to add them after the clouds and the Fox. The pipes are in the foreground. As I wrote about the last time, the order you define SVG object becomes the z-order. The last one defined is the object in front of the rest. We want the clouds behind the pipes and the Fox might as well be there too.

Here's the code for the two pipes:

    // Pipes
    // Upper Pipe
    // Define the pipe.
    uPipe = document.
      createElementNS(svgURL,"rect");
    // Set the values.
    uPipe.x.baseVal.valueAsString =
      uPipeX + "px";
    uPipe.y.baseVal.valueAsString = "0px";
    uPipe.width.baseVal.valueAsString =
      pipeWidth + "px";
    uPipe.height.baseVal.valueAsString =
      uPipeLength + "px";
    uPipe.style.setProperty("fill","red","");
    // Attach it to the board.
    gameBoard.appendChild(uPipe);

    // Lower Pipe
    // Define the pipe.
    lPipe = document.
      createElementNS(svgURL,"rect");
    // Set the values.
    lPipe.x.baseVal.valueAsString =
      uPipeX + "px";
    lPipe.y.baseVal.valueAsString =
      uPipeLength + 100 + "px";
    lPipe.width.baseVal.valueAsString =
      pipeWidth + "px";
    lPipe.height.baseVal.valueAsString =
      uPipeLength + "px";
    lPipe.style.setProperty("fill","red","");
    // Attach it to the board.
    gameBoard.appendChild(lPipe);

Moving the Pipes

This is in two parts. The first part is a call to a pipe moving function from the game loop. This call takes place every time through the loop. This line does it:

    movePipes();

The actual function both moves the pipes and checks for collisions. First we move the pipes and check to see if the pipes move off the left edge. If they do, we start them over again but change the lengths by a random amount so that the pipes give a different challenge every time the Fox encounters them. In addition, a check is made to make sure that the pipes aren't too short or too tall. When making a game, you have to think of every possibility and deal with it.

Here's the code for moving the pipes, checking for off-screen motion, resizing the pipes, and then checking to make sure that the resizing went okay.

  // Move the pipes.
  function movePipes() {

    // Move the pipe X value. 
    uPipeX = uPipeX - 2;
   
    // Check if pipes off left edge.
    if (uPipeX < -50) {
   
      // If too far, send to right side.
      uPipeX = boardWidth;
     
      // Resize pipes.
      // Generate random change.
      change =
        Math.
          floor((Math.random()*60)+1) - 30;
       
     
      // Calculate new pipe lengths.
      uPipeLength = uPipeLength + change;
      lPipeLength = lPipeLength - change;
   
      // If upper pipe is too short, back up.
      if (uPipeLength < 100) {
        uPipeLength = uPipeLength - change;
        lPipeLength = lPipeLength + change;
        console.log("UPPER PIPE TOO SHORT");
      }   
     
      // If lower pipe is too short, back up.   
      if ((uPipeLength + 100) >
          (boardHeight)) {
        uPipeLength = uPipeLength - change;
        lPipeLength = lPipeLength + change;
        console.log("LOWER PIPE TOO SHORT");
      }
           
      // Draw upper pipe if new size.
      uPipe.height.baseVal.valueAsString =
        uPipeLength  + "px";
     
      // Draw lower pipe if new size.     
      lPipe.y.baseVal.valueAsString =
        uPipeLength + 150 + "px";
      lPipe.height.baseVal.valueAsString =
        lPipeLength + "px";
      
      // Display current values.      
      console.log(uPipeLength,
        lPipeLength, change);
    }


Essentially here is how it goes:

  1. The pipe moves 2 pixels to the left.
  2. If the pipes go off the left edge, the pipes are resized.
  3. Is the upper pipe too short? If so, ignore the change.
  4. Is the lower pipe too short? If so, ignore the change.
  5. If all is well, draw the upper pipe.
  6. If all is well, draw the lower pipe, basing it on the upper pipe.

Note: the pipes are drawn in this part only if they are being resized. Otherwise the pipes are redrawn every time the game loop runs and that code will be at the end of the function.

Testing for Collision

Still in the same function, we need to now test to see if the pipe hit the Fox (which is the same as the Fox hitting the pipe, but because the Fox is flying, it is easier to test from the pipe's point of view). Here are the tests, base on simple calculation.

    // Test for upper pipe collision.
    if ((foxX > uPipeX) &&
        (foxX < (uPipeX + 50)) &&
        (foxY < uPipeLength)
        ){
     
      console.log("Hit upper pipe. Ouch!");
     
      // Game is over.
      gameOver();
    }

    // Test for lower pipe collision.
    if ((foxX > uPipeX) &&
        (foxX < (uPipeX + 50)) &&
        (foxY > (boardHeight - lPipeLength))
        ){
     
      console.log("Hit lower pipe. Ouch!");

      // Game is over.
      gameOver();
    }


This simply check for the x and y values of the Fox and each pipe. It is simpler to test each pipe separately. If they collide, the game is over and a separate function is called. Note that I've used the console.log here a lot. It was invaluable in determining whether things collide and where they are. Don't leave home without it!

Draw the Pipes Every Time

The pipes need to be drawn so they move every time through the game loop. But this is separate from the drawing that takes place if the pipes move off the left edge. Here's the simple drawing:

    uPipe.x.baseVal.valueAsString = uPipeX;
    lPipe.x.baseVal.valueAsString = uPipeX;
  }


Ending the Game
I had fun deciding how to do this. When there is a collision with the pipes or the Fox hits the top or bottom of the screen, the gameOver function is called. This just does two things:

  1. Make the Fox big. Really big. He blows up!
  2. Then a quick timer call takes place so the Fox won't stay blown up too long.

Here's the code for that:

// Game over!
  function gameOver() {

    // Make the fox big!
    myFox.setAttribute("height",200);
    myFox.setAttribute("width",200);
       
    //Wait wait a short time!
    foxBlowUpTimer =
      setTimeout(foxBlowUp, 700);
  }


And the final code sets the Fox back to normal size and puts him back to the starting position. The game continues. Here's that code:

  // Fox blow up!
  function foxBlowUp() { 
   
    // Reset fox values.
    foxX = boardWidth / 2 - 100;
    foxY = boardHeight / 2;
    foxW = 50;
    foxH = 50;
   
    // Reset fox position.
    myFox.setAttribute("height",foxH);
    myFox.setAttribute("width",foxW);
    myFox.setAttribute("x",foxX);
    myFox.setAttribute("y",foxY); 
  }


So that's it for the Flying Fox! I hope you've enjoyed this walk through a simple game and if you get a chance, check out all the other games that were inspired by Flappy Bird at http://itch.io/jam/flappyjam.

Thursday, November 21, 2013

CSS Detecting Collisions (Game Programming)

One of the foundations for any kind of action game is the ability to detect collisions. Did your spaceship crash into the asteroid or not? How about your fancy race car?



There are three ways that I know of to do this, depending on the technology you are using (CSS, Canvas, or SVG).

The first and definitely the most popular is to calculate if one object's boundaries overlap with another object's boundaries. This is called bounding boxes and is simple, but involves some minor calculation. In the example below, I'm making it simple and only comparing the top left corner of two same-sized square objects. Do they match or not?

Maybe not that exciting, but the example also shows how to move a character automatically and also uses three different timing loops. The stars of this show are:

1. A ball. You've seen it before.



2. A box. He's new. And wants to kill the ball. Don't ask why. Boxes just do that.





3. The ground. This isn't a space game, so we need a patch of ground for the battle to take place:


Maybe not the most exciting, but my philosophy about understanding game programming concepts is to keep it as simple as it can be and still show something. So we've got a ball and a box and they're sitting on the ground.

Here's what the game looks like:


The square tries to kill the poor little ball. But the ball has a secret power. Well, probably not too secret if you read earlier posts. The ball can bounce!


Touch the screen anywhere and the ball will jump up in the air for one second. This makes it a game. You must make the ball jump up just before the box would collide with you. If you fall back down again and the box collides with you, you're dead.

If you don't make it and the box collides with you, an alert is displayed that says "Boom".

So here's the code. It has been tested and run on the ZTE Open with 1.0 Firefox OS.

<!DOCTYPE HTML>
<html>
 
  <head>
    <meta charset="UTF-8">
    <title>
      CSS Collision
    </title>
 
    <style>
        
      #ball
      {
        width: 20px;
        height: 20px;
        position: absolute;
        top: 150px;
        left: 50px;
        background-image: url(ball.png);
      }
      
        #box
      {
        width: 20px;
        height: 20px;
        position: absolute;
        top: 150px;
        left: 270px;
        background-image: url(box.png);
            border-style: solid;
            border-color: black;
            border-width: 1px;
      }

        #ground
      {
        width: 320px;
        height: 230px;
        position: absolute;
        top: 170px;
        left: 0px;
        background-color: chocolate;
      }

    </style>
   
       <div id="ball"></div>
    <div id="box"></div>
      <div id="ground"></div>

    <script type="text/javascript">

      // Global variables    
      var boardWidth = 320;
      var boardHeight = 460;    
      var ballHor = 50;
      var ballVer = 150;     
      var howFast = 3;
        var boxHor = 270;
      var boxVer = 150;
      var loopID;
     
        // requestAnimationFrame browser variations
        var requestAnimationFrame =
          window.requestAnimationFrame ||
          window.mozRequestAnimationFrame ||
          window.webkitRequestAnimationFrame ||
          window.msRequestAnimationFrame; 
       
      var cancelAnimationFrame =
        window.cancelAnimationFrame ||
        window.mozCancelAnimationFrame;
     
        // Load event listener
      window.addEventListener("load", getLoaded, false);
      window.addEventListener(
          "mousedown", ballJump, false);

        // Get box and ball information.
        var myBall = document.querySelector("#ball");
        var myBox = document.querySelector("#box");
        var myGround = document.querySelector("#ground");
     
      // Function called on page load.
      function getLoaded() { 

            // Output to console.
        console.log("The page is loaded.");
       
          // Start game loop.
            gameLoop();              
      }
     
        // Game loop
        function gameLoop() {
       
            // Loop within the loop.
            setTimeout(function() {
       
          loopID = requestAnimationFrame(gameLoop);
         
          // Drawing code goes here
              console.log("Moving the box.");
         
              // Move the box
              moveBox();
         
        }, 1000 / howFast); // Delay / how fast
          }

      // Move the box here.
      function moveBox() {
     
          // Subtract 10 from box horizontal value.
          boxHor = boxHor - 10;
     
          // Move the box 10 pixels left.
          myBox.style.left = boxHor + "px";
       
          // If the box hits the left edge, restart.
          if (boxHor < 10) boxHor = 270;   

          // Report position of box.
          console.log(boxHor);
     
      // Check for collision.
      if ((boxHor == ballHor) && (boxVer == ballVer)) {
         
        // Collision took place!
        alert("Bang");
            
        window.cancelAnimationFrame(loopID);
      }
      }

        // Make the ball jump up.
    function ballJump() {
   
      // Calculate ball jump and move it.
      ballVer = ballVer - 50;
      myBall.style.top = ballVer + "px";
   
      console.log("Ball up.");
   
      // Make the ball fall after one second.
      ballTimer = setTimeout(ballFall, 1000);     
    }
   
    // Make the ball fall down.
    function ballFall() {
   
      // Calculate the ball fall and move it.
      ballVer = ballVer + 50;
      myBall.style.top = ballVer + "px";
   
      console.log("Ball down.");  
    }

    </script>
  </head>
 
  <body>
  </body>

</html>


I'm using the CSS shell that I wrote in this post: http://firefoxosgaming.blogspot.com/2013/11/css-shell-game-programming.html. The shell just provides the basic HTML5 structure, has a initial function that runs when the page loads, has a game loop, and then other functions that do specific things.

Style

Because this is CSS, I have an inline style that defines the properties of a ball, a box, and the ground. The ball is the same as what you've seen before. The box is just a square of color. I didn't like the way the box looked with just color, so I modified it in the style to have a black outline one pixel thick. The ball and the box were created in an art editor (Paint Shop Pro) and saved as PNG files. For the background, I decided to save room and just had it be a separate div with a background color (chocolate). This shows that there are three ways to make a graphic: import, import with modification, and fill a div with color.

To make these styles work, I had to create a separate div for each one, assign it a style, and then use querySelector to grab it and assign it a variable name. After that, I have three objects I can play with. The ground is just decoration and doesn't serve any programming purpose.

Game Loop

The game loop is called by the page load function. The game loop is interesting and uses two different timers to make sure that the box moves in regular steps as it marches across the screen.

The outer loop uses setTimeout to do something at regular intervals. In this case the interval is 1000 milliseconds (one second) divided by the howFast variable. In this case, howFast is 3, but you could use different values to make a game easier or harder. In this example, the "something" you do every 1/3 of a second is to move the box.

So, every 1/3 of a second, requestAnimationFrame is used to start the main loop over again, but also makes a call to actually draw the box (in a separate function, moveBox). Using requestAnimationFrame here makes sure that the drawing is done right away and helps with battery life. Whenever possible, use this for animations, especially repetitive ones like moving the box one step at a time.

Moving the Box

The box moves every 1/3 of a second. As it moves, it checks for two things:

1. Did I hit the left wall? If I did, move back to my initial position and try again.

2. Did I collide with the ball? If I did, then display an alert.

Step 2 is a bit tricky. You want to test for matching the top left corner of each object, and see if they both match. If they do, you've got a collision. The test is for this:

          ((boxHor == ballHor) && (boxVer == ballVer))

This line tests for three things:

  1. Does the horizontal (left) point of both box and ball match?
  2. Does the vertical (top) point of both box and ball match?
  3. Are both of these true?
The && is a symbol that means both of these things are true, and the placement of parentheses is important so you know what you're comparing and that they fit into the if statement.

Collision Test

If the top left of both match, we have a collision. Where things are moving in different directions or are of different sizes, you might figure out what the bounding box would be of each object and then compare them. A bounding box is an imaginary square that encloses the part of the object you want to test for. Maybe you only care about the center of your spaceship and so you have a small bounding box. The point is that you care and then by subtraction, you see if the two boxes overlap. Here because I kept the movement and size simple, you're only caring about the top left corner of each object, and the moving object is moving at a regular pace that will always match the position of the ball in its steady march of 10 pixels left every 1/3 of a second.

Bouncing Balls

You knew we had to have a bouncing ball somewhere. What makes a game is the choice the player makes to avoid or capture or destroy the enemy. Because this is a phone game, you make the ball jump up by tapping the screen anywhere. The easy way to do this is by using the mousedown event, which gets translated to a touch by Firefox OS. Don't use click, which requires the mouse to go down and then up.

If you touch the screen and fire the mousedown event, the ballJump function is called. This will draw the ball 50 pixels above where it was (don't forget that you're measuring pixels from the top left corner of the screen, so up is smaller numbers and down is larger numbers).

setTimeout (a different timer than the setTimeout one in the game loop) is called that will run for one second (1000 milliseconds). After the second has passed, the ballFall function is called. This simply changes the ball position 50 pixels below where it was (by adding 50 to it -- up is down, down is up).

Timers

This example uses three timers:

1. The first is the 1/3 second delay every time the game loop is processed. Slow.

2. The second is the call using requestAnimationFrame, which draws the box as fast as the browser can, but is more efficient than setTimeout. Fast.

3. The third is the one-second delay that the ball spends hanging in air before it falls back down. Slow.

The first and third use setTimeout, which is okay because you don't need precision timing or power efficiency. The second uses requestAnimationFrame because anything that uses repetitive drawing (like marching across the screen) will do well here. Of course this example is pretty simple, but as you move on to more complex animations, this kind of looping will be very useful. Slow timing is fine for setTimeout, but requestAnimationFrame is better for fast and efficient motion.

There is a great article about using both of these timing methods (and more) at http://creativejs.com/resources/requestanimationframe/
 
requestAnimationFrame and close

I thought it would be nice to stop the game when the collision took place. But I found that there is some kind of problem with using cancelAnimationFrame. I coujldn't get it to work. This function is supposed to stop the animation frame loop (which is used to reset the game loop). It works just fine in Firefox browser, but does NOT work on the ZTE.

The above code that I used for this was straight from the MDN https://developer.mozilla.org/en-US/docs/Web/API/window.cancelAnimationFrame page.  I might be doing something wrong or I left out a semicolon. All I can say is that I copy and pasted this from this page, which should allow either cancelAnimationFrame or mozCancelAnimationFrame, and does work in the big browser (go Aurora!) but doesn't work on my poor old ZTE Open stuck at Firefox OS 1.0. If someone wants to tell me what is supposed to work when, I'll be happy to post the changes needed to make this work.

I also thought that well, if I can't kill the animation timer, maybe I can just close the window. It turns out that Firefox doesn't support window.close() unless you opened that window yourself. Probably a good idea. But I would like to find a way to shut down an app. Where's that task manager when you need it?

But the principles of the rest of the code are just fine and having the marching repeat endlessly can be considered a feature, not a bug. (Ha ha!)

You can apply this type of collision testing to any of the three technologies (CSS, Canvas, and SVG). Decide on your bounding box coordinates and test for boundary overlaps.

But there is ... another. In fact, two others. One for Canvas and the other for SVG. I'll do the Canvas thing next and then SVG.

WebGL

Note: when I started this series, the three technologies of CSS, Canvas, and SVG were all that seemed currently useful. It now looks like some serious WebGL technologies are ready for prime time. In the last few days there have been two cool advances in WebGL.

WebGL is a technology that takes the core of OpenGL and applies it to web programming to create cool visual effects. OpenGL is used by most 3D game programmers but was claimed by Microsoft to be evil (because DirectX was better). Then, in a move that surprised a lot of folks, Microsoft included WebGL in Internet Explorer 11. Maybe not as good as Firefox's WebGL, but at least they're not saying it is evil any longer.  So, like SVG, once Microsoft says it is okay, the rest of the world can jump in and play.

The first news about WebGL is that Firefox now has a cool way to debug and edit WebGL in the browser itself. Check out this post: https://hacks.mozilla.org/2013/11/live-editing-webgl-shaders-with-firefox-developer-tools/. WebGL (or any 3D graphics) isn't easy to understand, but I'll be curious to see people using it on Firefox OS.

The second bit of news about WebGL is that Sony is using WebGL for their user interface for the Playstation 4 for the store, music player, and other junk. See this post by one of the developers (a very smart guy) https://plus.google.com/+DonOlmstead/posts/Mzy6VEAwHaa. I can't wait to see it (oh, wait, I don't have a Playstation 4. But I'm too busy playing Firefox OS games to care).

WebGL sits on top of Canvas, so maybe when I finish writing about all the useful aspects of 2D games written in CSS, Canvas, or SVG, I'll start tackling WebGL. I have done some programming in DirectX and OpenGL (and even a little WebGL), but I like 2D programming better.

Stay tuned, but not iTuned!