Showing posts with label CSS Shell. Show all posts
Showing posts with label CSS Shell. Show all posts

Wednesday, March 26, 2014

CSS Shooter (Game Programming)

Recently I created a new CSS shell and wrote about it in Son of CSS Shell at http://firefoxosgaming.blogspot.com/2014/03/son-of-css-shell-game-programming.html. Having done that, I'd like to devote a few posts to working more with just plain old CSS. No Canvas, no SVG, not even CSS3. Some people call this CSS sprites but I'll just call it CSS game programming. And I'll be doing more with actual CSS sprites, which are a collection of images on a single file.

Like this:


But that's later. Right now I'm interested in doing more actual gaming with CSS. Today's game is a simple one, that is a shooter. Inspired by Breakout but with only one brick.


But then I was also inspired by the recent release of Yoshi's Island on the Nintendo 3DS and actually inspired by the earlier version on the Game Boy Advance that used a special moving cursor to aim shots.






Yoshi is in the center and the cursor (on the top right) moves up and down in an arc. At the right moment you press a button and Yoshi's egg flies in that direction! Well, I'm not going to replicate Yoshi's Island but I will say that the best version is on the Game Boy Advance and it looks like that version will be appearing on the Wii U Virtual Console very soon.

So I created a very simple shooter that aims a bullet from the bottom of the screen and shoots it at a target near the top of the screen. And to make it more challenging, the top target moves from left to right across the screen, while the bullet (until fired) moves back and forth. Just click on the screen at the right moment and you can hit the target.

Here's what the game looks like in play.


Tap anywhere to make the fuchsia bullet shoot up.


The bullet is on its way to the top of the screen. In this case, it looks like it is going to hit the green target. And it does! An alert is displayed telling you that you hit the target.


And if you didn't hit the target (easy to do since the cursor moves one way and the target another) you'll be told that you missed, but in either case you can start over again by just tapping on OK.


The Firefox OS App Manager makes it really easy to test and take screen shots. 

This is cool, but I had even more fun creating it by using the desktop of Firefox Nightly. As explained in the Son of CSS Shell post at http://firefoxosgaming.blogspot.com/2014/03/son-of-css-shell-game-programming.html, I created a simple shell that is responsive to whatever size the browser happens to be. So because this game doesn't have anything phone-specific, I ran it in the desktop version and resized it to vaguely phone size. I also hit Control+Shift+K to get the debugger going, and here is what it looks like:


I've just had a hit, but you can see the debugging output that shows me the target and ball matching, and the subsequent collision. You can also see the bullet and target colliding in the upper right-hand corner.

Working this way is very, very fast when you are creating, and if you're not doing something that requires a phone, I recommend it. Of course all  bets are off if you're doing Device Orientation!

Here's a similar screen showing the debugging output of the desktop browser for a miss. You can see that the bullet is on the left, far away from the target in the middle.


Firefox OS makes it so easy to make games that you have no excuse not to!

Code

And here is the code that makes this happen:

<!DOCTYPE HTML>
<html>
<head>
  <meta charset="UTF-8">
  <title>
    Simple CSS Shoot
  </title>

  <style>
     
    #ball
    {
      width: 20px;
      height: 20px;
      position: absolute;
      top: 200px;
      left: 100px;
      background-image: url(ball.png);
    }
   
    #target
    {
      width: 20px;
      height: 20px;
      position: absolute;
      top: 200px;
      left: 100px;
      background-color: green;  
    }

  </style>

  <div id="ball"></div>   
  <div id="target"></div>

  <script>

    // Global variables   
    var boardWidth = window.innerWidth;
    var boardHeight = window.innerHeight;   
    var ballHor = boardWidth / 2;
    var ballVer = boardHeight - 50; 
    var tarHor = boardWidth / 2;
    var tarVer = 50;   
    var changeHor = 10;
    var changeVer = 10;
   
    // Log initial board height & width.
    console.log("Board width = "
      + boardWidth);
    console.log("Board height = "
      + boardHeight);
  
    // requestAnimationFrame variations
    var requestAnimationFrame =
    window.requestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.msRequestAnimationFrame;
   
    // Stop scroll bars from interfering.
    document.documentElement.
      style.overflow = 'hidden';
  
    // Load event listener
    window.addEventListener("load",
      getLoaded, false);
     
    // Resize event listener
    window.addEventListener("resize",
      resizeMe, false);
     
    // Mouse down event listener
    window.addEventListener(
      "mousedown",
         bulletFly, false);

    // Get target information.
    var myTarget =
      document.querySelector("#target");

    // Get ball information.
    var myBall =
      document.querySelector("#ball");
  
    // Function called on page load.
    function getLoaded() {
   
      // Lock screen orientation to portrait.
      // Uses "moz" prefix.
      window.screen.
        mozLockOrientation("portrait"); 
      console.log("Locked to portrait");
     
      // Background color
      document.body.style.
        backgroundColor = "Peru";
  
      // Start game loop.
      gameLoop();   
      
      // Page loaded message
      console.log("The page is loaded."); 
    }
  
    // Game loop
    function gameLoop() {
  
      // Repeat game loop infinitely.
      animFrame =
        requestAnimationFrame(gameLoop);
  
      // The ball is now a bullet.
      bulletMove();      
     
      // Move the target.
      targetMove();
      }
  
    // Make the target move.
    function targetMove() {
   
      // Changes are calculated but do not
      // take effect until next time.     
      myTarget.style.left = tarHor + "px";
      myTarget.style.top = tarVer + "px";   

      // Calculate new horizontal component.
      tarHor = tarHor + 10;
    
      // Right edge hit, restart.
      if (tarHor > boardWidth)
          tarHor = 0;
    }
  
    // The bullet flies up.
    function bulletFly() {
   
      // Stop animation loop!
      cancelAnimationFrame(animFrame);    
     
      // Delay for animation motion
      // Outer loop - time set at end     
      loopTimer = setTimeout(function() {
     
        // Animation of the bullet
        // Inner loop - does the drawing
        bulletFrame =
          requestAnimationFrame(bulletFly); 
         
         // Calculate bullet fly up.
        ballVer = ballVer - 50;  
       
        // Changes are calculated but do not
        // take effect until next time.     
        myBall.style.left = ballHor + "px";
        myBall.style.top = ballVer + "px";   

        // Track bullet and target
        console.log("Ball: " + ballVer +
          " ," + ballHor + " Target: " +
          tarHor);
         
        // TEST - REMOVE
        // ballHor = tarHor;
       
        // Test for collision.
        // Horizontal the same
        // Vertical close to top.    
        if  (((ballHor < (tarHor + 20)) &&
              (ballHor > (tarHor - 20))) &&
          (ballVer < 60)) {
             
          // It's a hit!
          console.log("Collision");
          alert("You hit! Start over!");
         
          // Reset everything.
          startOver();
        }
       
        // Test for missing.
        // Vertical less than 50.
        if (ballVer < 60) {
          console.log("Missed!");
          alert("Missed! Start Over!");
         
          // Reset everything.
          startOver();
        }  
      } , 300); // Delay
    }
   
    // Reset everything and start over!
    function startOver(){
   
      // Reset ball and target.
      ballHor = boardWidth / 2;
      ballVer = boardHeight - 50; 
      tarHor = boardWidth / 2;
      tarVer = 50;   
     
      // Stop the drawing.
      cancelAnimationFrame(bulletFrame);
     
      // Stop the loop.
      clearTimeout(loopTimer);
     
      //Go to the game loop and start over.
      gameLoop();
    }
   
    // Calculate and move the ball. 
    function bulletMove() {
  
      // Changes are calculated but do not
      // take effect until next time.     
      myBall.style.left = ballHor + "px";
      myBall.style.top = ballVer + "px";   

      // Calculate new horizontal component.
      ballHor = ballHor + changeHor;

      // Left edge hit, reverse direction.
      if (ballHor + changeHor < -10) {
        changeHor = -changeHor;
        ballHor = 10;
      }
    
      // Right edge hit, reverse direction.
      if (ballHor + changeHor >
        boardWidth - 10) {
          changeHor = -changeHor;
          // Compensate for unknown width.
          // We want multiples of 10.
          flr = Math.floor(boardWidth / 10);
          ballHor = 10 * flr;        
      }
    }

    // Testing in desktop browser only.
    // Changes board size to match new.
    function resizeMe() {
     
      //Change board size.
      boardWidth = window.innerWidth;
      boardHeight = window.innerHeight;  
     
      // Log initial board height & width.
      console.log("Board width = "
        + boardWidth);
      console.log("Board height = "
        + boardHeight);
    }
   
  </script>
</head>

<body>
  <footer>
    <small>
      Copyright &copy; 2014 Bob Thulfram
    </small>
  </footer>
</body>
</html>


This uses the Son of CSS shell, so I won't explain any of that here! But here are the new parts that you might like to know about.

Target CSS

I defined the target in CSS like this:

    #target
    {
      width: 20px;
      height: 20px;
      position: absolute;
      top: 200px;
      left: 100px;
      background-color: green;  
    }


Similar to the bullet, but instead of using an imported PNG image, I just used the power of CSS to create a box and give it the background color of green. If you want boxes, CSS can turn them out all day.

I also then gave the target a place in the browser DOM by making a DIV. Remember, there's no code in the body of the page!

  <div id="target"></div>

I also had to select the target to make it known to everybody.

    var myTarget =
      document.querySelector("#target");


This might seem like a strange way to do things, but it works in every browser, and especially in Firefox OS. Just do these things to introduce your CSS objects to the DOM and you can do anything you want with them. Follow these steps:

  1. Define the CSS object using CSS.
  2. Create a DIV with the ID of the CSS object you just defined.
  3. Select the object with querySelector and your CSS object is known to all.

Global

Then I added a few things to the global section that will be useful later:

    var tarHor = boardWidth / 2;
    var tarVer = 50; 


This puts the target near the top. It's horizontal will change when the game starts.  

I also changed a global for the ball:

    var ballVer = boardHeight - 50; 

This makes the vertical position of the ball near the bottom. The horizontal will change when the game starts.

Note: in the code, the bullet is called ball because I kept the old code from the shell. I don't like to change variable names if I don't have to. I hope you don't find it too confusing.

Touch Event

Next I added a touch event, also known as mousedown. The phone will interpret this as a touch event.

    window.addEventListener(
      "mousedown",
         bulletFly, false);


This will create an event listener which will call the bulletFly function when the screen is touched anywhere. And works with mouse clicks in the desktop browser. Two events for the price of one!

Game Loop

Add this function call to the game loop:

  bulletMove();

It was called ballMove() in the shell. It will move the bullet back and forth every time the game loop runs (governed by requestAnimationFrame).

Then add  the function call to move the target with this:

  targetMove();

Moving the Target

This moves the target!

    function targetMove() {
       
      myTarget.style.left = tarHor + "px";
      myTarget.style.top = tarVer + "px";   

      tarHor = tarHor + 10;
    
      if (tarHor > boardWidth)
          tarHor = 0;
    }


This just makes the target move from left to right. The target is drawn but the changes won't take effect until the next time through.

The target horizontal position is incremented by 10 and then a check is made. If the target would go off the right edge, the horizontal position is set to 0.

Moving the Bullet

The bullet moves right to left and then left to right. It makes the game more challenging to have two different motions.

    function bulletMove() {
  
      myBall.style.left = ballHor + "px";
      myBall.style.top = ballVer + "px";   

      ballHor = ballHor + changeHor;

      if (ballHor + changeHor < -10) {
        changeHor = -changeHor;
        ballHor = 10;
      }
    
      if (ballHor + changeHor >
        boardWidth - 10) {
          changeHor = -changeHor;
          flr = Math.floor(boardWidth / 10);
          ballHor = 10 * flr;        
      }
    }


This starts out the same as the ballMove function in the Son of CSS shell. But because it only goes left and right and doesn't hit the top or bottom, it just checks for the left and right edges.

If the left edge is hit, the direction changes and the bullet horizontal is set to 10.

But if the right edge is hit, the direction is changed, but a quick calculation takes place to make sure that the new bullet horizontal position is set to a multiple of 10. Because we can't know the exact screen size, this is one of the fun parts of making a responsive design.

Shooting the Bullet

This is the meat of the program. It does a lot of things!

    function bulletFly() {
   
      cancelAnimationFrame(animFrame);    
     
      loopTimer = setTimeout(function() {
     
        bulletFrame =
          requestAnimationFrame(bulletFly); 
         
        ballVer = ballVer - 50;  
       
        myBall.style.left = ballHor + "px";
        myBall.style.top = ballVer + "px";   

        console.log("Ball: " + ballVer +
          " ," + ballHor + " Target: " +
          tarHor);
         
        // TEST - REMOVE
        // ballHor = tarHor;
       
        if  (((ballHor < (tarHor + 20)) &&
              (ballHor > (tarHor - 20))) &&
          (ballVer < 60)) {
             
          console.log("Collision");
          alert("You hit! Start over!");
         
          startOver();
        }
       
        if (ballVer < 60) {
          console.log("Missed!");
          alert("Missed! Start Over!");
         
          startOver();
        }  
      } , 300); // Delay
    }


First the requestAnimationFrame used in the game loop is canceled. Once the bullet starts on its upward journey, you don't want the game loop to run any longer.

Next, a new loop is created, one that will provide a short delay between each motion of the bullet as it flies up. This loop uses setTimeout with an anonymous function. Inside that function is an inner loop that just runs a different requestAnimationFrame.

This will work in a lot of cases where you want motion. You calculate the motion, have a short delay in an outer loop, and then in an inner loop you use requestAnimationFrame to do the actual drawing. This is very efficient.

Once the loop is running, every time through is will move the bullet up 50 pixels and draw it. I added a console.log here so I could track the bullet.

Then I tested to see if a collision had taken place. This is a bit tricky, and watch those parentheses. I recommend you use an editor that will check parentheses! I use notepad++ and I'm very happy with it.

The calculation sees if the horizontal positions of the bullet and target are within a certain range of each other and then checks the vertical. The && is just a logical AND and says these three conditions have to be true:

  1. The bullet and target are close on one side horizontally.
  2. The bullet and target are close on the other side horizontally.
  3. The bullet is high enough to be close vertically to the target.

If all three are true, you have a collision. A console.log is displayed, an alert is displayed, and the startOver function is called to restart the game. 

If that doesn't happen, then a second check happens to see if the bullet went off the top of the screen. If it did, a similar console.log, alert, and call to startOver takes place.

Note: I added

   ballHor = tarHor;


in my testing to make it easy to see if the bullet was hitting the target but then commented it out. Sometimes this is a useful trick when you are trying to test something in a fast moving game.

Starting Over

I created a separate startOver function because the tasks were shared between colliding and going off the top of the screen.

    function startOver(){
   
      ballHor = boardWidth / 2;
      ballVer = boardHeight - 50; 
      tarHor = boardWidth / 2;
      tarVer = 50;   
     
      cancelAnimationFrame(bulletFrame);
     
      clearTimeout(loopTimer);
     
      gameLoop();
    }


This just sets the initial position of the bullet and target. It then cancels the two loops that were used to move the bullet up, and calls the game loop.

That's it!













Thursday, March 20, 2014

Son of CSS Shell (Game Programming)

As I get further into programming for Firefox OS, I find that I want to go back to basics from time to time. One of my favorite things is to create a shell that can be used to build games on top of. A simple run of code that has all the basics in place. Taking what I've learned, I've created a new shell for you to use for CSS games. When I say CSS games, I am thinking of what some people call CSS sprites. Essentially you move images around on the screen by taking advantage of basic properties of HTML5, CSS, and JavaScript. Not CSS3, mind you, which has become a huge ball of stuff that looks cool but doesn't hang together quite yet.

So here is what I recommend for starting out with CSS sprites. You can read more about those at these earlier posts:

Bouncing a Ball in CSS
http://firefoxosgaming.blogspot.com/2013/10/bouncing-ball-in-css-game-programming.html

Earlier CSS Shell
http://firefoxosgaming.blogspot.com/2013/11/css-shell-game-programming.html

CSS Collision Detection
http://firefoxosgaming.blogspot.com/2013/11/css-detecting-collisions-game.html

The shell has a simple bouncing ball, which is easy to see how it works, but just as easy to remove the bouncing ball part and add your own type of game.

Screen Shots

So, here's what this shell looks like, running on the ZTE Open.


Next, here is the same game running in the App Manager simulator.


And, finally, here's what it looks like in the nightly Firefox desktop browser with the debugger open and resized to a smaller-than-normal size.


The last is pretty useful if you want to instantly simulate different screen sizes and look at the debugging information. The screen is only 343x237, but the ball bounces just fine against the four edges but doesn't enter the debugger space.

The Code

Here is the code:

<!DOCTYPE HTML>
<html>
<head>
  <meta charset="UTF-8">
  <title>
    New CSS Shell
  </title>

  <style>
     
    #ball
    {
      width: 20px;
      height: 20px;
      position: absolute;
      top: 200px;
      left: 100px;
      background-image: url(ball.png);
    }

  </style>

  <div id="ball"></div>   

  <script>

    // Global variables   
    var boardWidth = window.innerWidth;
    var boardHeight = window.innerHeight;   
    var ballHor = boardWidth / 2;
    var ballVer = boardHeight /2;    
    var changeHor = 10;
    var changeVer = 10;
   
    // Log initial board height & width.
    console.log("Board width = "
      + boardWidth);
    console.log("Board height = "
      + boardHeight);
  
    // requestAnimationFrame variations
    var requestAnimationFrame =
    window.requestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.msRequestAnimationFrame;
   
    document.documentElement.
      style.overflow = 'hidden';
  
    // Load event listener
    window.addEventListener("load",
      getLoaded, false);
     
    // Resize event listener
    window.addEventListener("resize",
      resizeMe, false);

    // Get ball information.
    var myBall =
      document.querySelector("#ball");
  
    // Function called on page load.
    function getLoaded() {
   
      // Lock screen orientation to portrait.
      // Uses "moz" prefix.
      window.screen.
        mozLockOrientation("portrait"); 
      console.log("Locked to portrait");
     
      // Background color
      document.body.style.
        backgroundColor = "Peru";
  
      // Start game loop.
      gameLoop();   
      
      // Page loaded message
      console.log("The page is loaded.");
     

      }
  
    // Game loop
    function gameLoop() {
  
      // Repeat game loop infinitely.
      requestAnimationFrame(gameLoop);
  
      // Move the ball once.
      ballMove();      
      }
  

    // Calculate and move the ball. 
    function ballMove() {
  
      // Changes are calculated but do not
      // take effect until next time.     
      myBall.style.left = ballHor + "px";
      myBall.style.top = ballVer + "px";
      
      // Calculate new vertical component.
      ballVer = ballVer + changeVer;

      // Top hit, reverse direction.
      if (ballVer + changeVer < -10)
        changeVer = -changeVer;
    
      // Bottom hit, reverse direction.
      if (ballVer + changeVer >
        boardHeight - 10)
          changeVer = -changeVer;

      // Calculate new horizontal component.
      ballHor = ballHor + changeHor;

      // Left edge hit, reverse direction.
      if (ballHor + changeHor < -10)
        changeHor = -changeHor;
    
      // Right edge hit, reverse direction.
      if (ballHor + changeHor >
        boardWidth - 10)
          changeHor = -changeHor;
    }

    // Testing in desktop browser only.
    // Changes board size to match new.
    function resizeMe() {
     
      //Change board size.
      boardWidth = window.innerWidth;
      boardHeight = window.innerHeight;  
     
      // Log initial board height & width.
      console.log("Board width = "
        + boardWidth);
      console.log("Board height = "
        + boardHeight);
    }
   
  </script>
</head>

<body>
  <footer>
    <small>
      Copyright &copy; 2014 Bob Thulfram
    </small>
  </footer>
</body>
</html>


What I Have Added

After thinking about it over the past few weeks, here are a few things I've added:
  1. Screen Orientation. The game plays best in portrait mode.
  2. Responsive Design. The game should play on any size phone or tablet.
  3. More Responsive Design. I wanted to test how this looks in browsers on desktops.
  4. I wanted to add a copyright notice in the code.
  5. I felt like a background color helps the screen shot stand out in a blog post. 

Screen Orientation

I've written about Screen Orientation for Firefox OS at http://firefoxosgaming.blogspot.com/2013/11/screen-orientation-and-moz-prefixes.html. And it works! Of course, you need to test this on a real phone. When I tilt the phone with my code, the screen doesn't tilt, it stays oriented in portrait.

However, this doesn't seem to work on the App Manager simulator. There's a little button next to the home button at the bottom that looks like two folders overlapping. Click on that and the screen turns sideways. But it shouldn't! This might be a bug or it might be that this isn't supported yet in the simulator. But it works fine on a phone!

Here's the code:

      window.screen.
        mozLockOrientation("portrait"); 
      console.log("Locked to portrait");


Note that this needs the "moz" prefix for now.

Responsive Design

One thing that I've noticed on a lot of the games I've reviewed is that they don't look the same on different phones. The ZTE Open and the Geeksphone Peak have different sizes! But you want to design your game so it works on any size phone, tablet, or whatever. Maybe not watches, though.

So what I'm doing with this code is to get the actual screen size and make that the size of my game board. I wrote about this in an early topics called How Big A I? at http://firefoxosgaming.blogspot.com/2013/10/how-big-am-i-game-programming.html.

And here is how I implement it. These lines set up the game board to match the actual screen. Instead of a fixed value, I created board size by seeing what the actual screen size is. This is in the global variable section.

    var boardWidth = window.innerWidth;
    var boardHeight = window.innerHeight;   


I also added this to make sure what actually happens:

    console.log("Board width = "
      + boardWidth);
    console.log("Board height = "
      + boardHeight);


It's always a good idea to track what is happening, especially when testing different sized phones.

Really Responsive Design

I often run my code first in the latest Firefox nightly browser. I recommend this, even though you need to be careful because not everything in the desktop browser is in Firefox OS! But it makes the easy development cycle even easier and you can save the phone-specific parts for testing on an actual phone (like Screen Orientation).

I thought it would be fun to resize the browser so I can just drag it to see it small or large. And it is also easy to see the debugged output on a desktop browser.

So I added this code to handle the event of the browser being resized.

    // Resize event listener
    window.addEventListener("resize",
      resizeMe, false);


This will call the function resizeMe every time the browser is resized. I also added this code to prevent the scroll bars from trying to display when the ball bounces into the edge.

    document.documentElement.
      style.overflow = 'hidden';


And here's the function that is called when the window is resized.

    function resizeMe() {
     
      //Change board size.
      boardWidth = window.innerWidth;
      boardHeight = window.innerHeight;  
     
      // Log initial board height & width.
      console.log("Board width = "
        + boardWidth);
      console.log("Board height = "
        + boardHeight);
    }


This is the same code as was used in the global variables, but you need to call it again when the window is resized. And I like outputting the new width and height of the board so I can see what is going on. The third screen shot above shows this working beautifully.

Copyright

Not everyone cares, but I care about copyright. I'm happy to have you use my code in your own games, but I'd like to keep control over how it is published and where it appears. This doesn't have a standard yet, but a growing convention is to use the HTML5 tag small inside a footer tag. So I've added this to the code at the very end, the only thing that is in the actual body of the code.

  <footer>
    <small>
      Copyright &copy; 2014 Bob Thulfram
    </small>
  </footer>


The &copy; will produce a copyright symbol (c).

Background Color

I've used this before here and there, so here it is again.

      document.body.style.
        backgroundColor = "Peru";


I picked the color Peru for no particular reason except that's a nice country! Get your color names at https://developer.mozilla.org/en-US/docs/Web/CSS/color_value. When I did this, I realized that my bouncing ball PNG image had a white background, which didn't look good, so I had to redraw it by filling in all the white bits with Peru color. Which the MDN page will happily tell you is rgb(205, 133,  63). What this means is that the red, green, and blue percents are those three numbers. I plugged those numbers into Paint Shop Pro and the PNG now looks like this, in all its pixel-y greatness.


Wrapping Up

So this is the shell I will be using to create new CSS game bits and I recommend you use the parts you want. I'll do the same for Canvas and CSS but I might do some cool CSS stuff sooner, and there's games to review, and, and, and!



Monday, November 18, 2013

CSS Shell (Game Programming)

I went back and revised the shell that I am going to use for Firefox OS CSS programming. Some of it is the same, but I added the following:

requestAnimationFrame

Instead of using this timer:

        gameLoop = setInterval(ballMove, 16);

I'm using requestAnimationFrame with a shim:

      // requestAnimationFrame browser variations
      var requestAnimationFrame =
      window.requestAnimationFrame ||
      window.mozRequestAnimationFrame ||
      window.webkitRequestAnimationFrame ||
      window.msRequestAnimationFrame; 


This makes for smoother animation and better battery life. Basically you're letting Firefox OS decide how fast the loop will repeat. And if you're running this in webkit or IE9, it will also work.

To use requestAnimationFrame, simply put it in the endless loop you want to use for your game loop. This will allow the loop to repeat, making things happen on a timely basis. Just put this in your loop and call the loop once:

      requestAnimationFrame(gameLoop);

The other major change is to create an event handler for page loading. The onXXX functions still work, but using event handlers is easier to use, debug, and cancel. So instead of:

      <body onload="playBall()">

Use this:

      window.addEventListener("load", getLoaded, false);

I like to have a standardized starting set of code which I call a shell. I'll be posting them as I go through a series of coding for collision testing for CSS, Canvas, and SVG.

Below is the code for the whole page. By the way, this was fun to run in Firefox OS and watch the Inspector display the coordinates of the ball in the DOM. Even though no code seems to run in the body, when the code runs, the Inspector shows you the rapidly changing:

      <div id="ball" style="left: 150px; top: 400px;"></div>

Find that in Web Developer -> Inspector. Also cool is that when in the Inspector mode, a little caption follows the ball around telling you that this is div#ball.


I'm starting to explore the new Firefox tools and how they work for debugging. Lots to learn.

So here's the code for my CSS Shell, as usual, tested and run in the little-but-loud ZTE Open, running Firefox OS 1.01.

<!DOCTYPE HTML>
<html>
 
  <head>
    <meta charset="UTF-8">
    <title>
      CSS Collision
    </title>
 
    <style>
        
      #ball
      {
        width: 20px;
        height: 20px;
        position: absolute;
        top: 200px;
        left: 100px;
        background-image: url(ball.png);
       }

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

    <script type="text/javascript">

    // Global variables    
      var boardWidth = 320;
      var boardHeight = 460;    
      var ballHor = boardWidth / 2;
      var ballVer = boardHeight /2;     
      var changeHor = 10;
      var changeVer = 10;
     
      // requestAnimationFrame browser variations
      var requestAnimationFrame =
      window.requestAnimationFrame ||
      window.mozRequestAnimationFrame ||
      window.webkitRequestAnimationFrame ||
      window.msRequestAnimationFrame; 
     
      // Load event listener
      window.addEventListener("load", getLoaded, false);

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

        // requestAnimationFrame for game loop
        requestAnimationFrame(gameLoop);
       
        // Output to debugger.
        console.log("The page is loaded.");
        }
     
      // Game loop
      function gameLoop() {
     
        // Repeat game loop infinitely.
        requestAnimationFrame(gameLoop);
     
        // Move the ball once.
        ballMove();       
        }
     

      // Calculate and move the ball.  
      function ballMove() {
     
        // Changes are calculated but do not
        // take effect until next time through loop.      
        myBall.style.left = ballHor + "px";
        myBall.style.top = ballVer + "px";
         
        // Calculate new vertical component.
        ballVer = ballVer + changeVer;

        // Top hit, reverse direction.
        if (ballVer + changeVer < -10)
          changeVer = -changeVer;
       
        // Bottom hit, reverse direction.
        if (ballVer + changeVer > boardHeight - 10)
          changeVer = -changeVer;

        // Calculate new horizontal component.
        ballHor = ballHor + changeHor;

        // Left edge hit, reverse direction.
        if (ballHor + changeHor < -10)
          changeHor = -changeHor;
       
        // Right edge hit, reverse direction.
        if (ballHor + changeHor > boardWidth - 10)
          changeHor = -changeHor;
        }

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

</html>