Monday, October 28, 2013

Bouncing a Ball in CSS (Game Programming)

I'm going to start a series looking at the 6 ways you can use simple graphics for games in Firefox OS.

What's that you say? Isn't Canvas the way to do graphics for games? Six ways? Simple?

Well, maybe not always simple, but here are the ways I'm going to demonstrate, in between reviewing other people's games:
  1. CSS sprites - easy for some games, works in every browser except maybe some really old ones.
  2. Canvas using bitmaps - this is the most popular. Blast a bitmap, you're done, almost.
  3. Canvas using lines and shapes - advanced, but might be cool with some kinds of games.
  4. SVG body - create SVG shapes in the body and move them around.
  5. SVG in the HTML DOM - create SVG shapes in the HTML5 DOM and manipulate them.
  6. SVG in the SVG DOM - SVG has its own DOM - "It's a secret to everybody" (Zelda)
But for today, you can use CSS (Cascading Style Sheets) to move bitmaps around on the screen. CSS has been around a long time, but most people think of it as a way to define the typefaces, layout, and colors of a web page. But you can use CSS to define graphics that you can move around. I won't use it in this post, but the coolest part of CSS is something called CSS Sprites, which lets you do simple animation.

So I've written a really simple program that just lets a ball bounce around your Firefox OS screen. You can't do anything but watch. It's just a demonstration of one way to do graphics. I'll use the same model for the other 5 ways to do graphics (and I'm not counting WebGL which is a cool 3D technology and I'm not using any JavaScript libraries either. (I tend to avoid JavaScript libraries because I like to know every detail of what is going on.)

Here is the code I've written. Copy it, slap it into a text file, use the same manifest as before, even the same icon. You can of course change the icon and you should change the personal details in the manifest as well.

But before you see the code, a bit about the Firefox OS development cycle. When you start up the Firefox OS Simulator, you pick a folder that has the manifest in it. If you want to make a change to your code, you need to:
  1. Stop the simulator (click "Running" and it will say "Stopped").
  2. Click on the x at the very far right to remove the app from the simulator.
  3. Reload the simulator (F5 is your friend).
In addition, if you have pushed your app to the phone, you will want to delete that app before you try to push a new version again. Do that by long-pressing on the app, touch the little x, allow your app to be killed, and then press the Home button to make the other apps stop wiggling (in fear that they will be killed next).

Now you are ready to put in this code (in a file named index.html):

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

    </style>
   
    <div id="ball" />   
   
    <script type="text/javascript">

    // Global variables
     
      var boardWidth = window.innerWidth;
      var boardHeight = window.innerHeight;
     
      var ballHor = boardWidth / 2;
      var ballVer = boardHeight /2;
     
      var changeHor = 10;
      var changeVer = 10;

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

        // Play the game until the ball stops.
        gameLoop = setInterval(ballMove, 16);
       
        // Output to debugger.
        console.log("The page is loaded.");

      }

      // 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 onload="playBall()">
  </body>

</html>


The code (unlike Gaul) is divided into 5 parts:
  1. The HTML5 shell, which just sets up the document for Firefox OS.
  2. The CSS definition, which defines the ball.
  3. A div element, which gives the ball a place to live.
  4. A script, which will set things up, create a game loop, and move the ball.
  5. The body, which loads the script named playBall.
Here's what the ball looks like on my ZTE Open:


It is purple and bounces. The ball is a 20x20 bitmap with transparent edges. It's really a square but is pretending to be a circle.

An interesting thing about the bitmap is that it is in the same folder as your manifest and index.html files. You don't need to tell the manifest to bring it along, it magically grabs the file referred to in the CSS url section and knows where it is. Firefox OS makes it so easy.

Here's a brief description of the parts of the app:
  1. All Firefox OS apps must have an HTML5 code structure. In this case, I've made sure I have a head and a body, and I've set the charset to UTF-8. Titles are good too.
  2.  Next I set up the CSS definitiion of the ball. CSS is a markup language that is pretty easy to learn. In this case I've just created something called "ball" that has width, height, position type, top, left, and background image defined. The background image is defined by a bitmap called "ball.png."
  3. Next I created a simple div element. Interestingly enough, I put it in the head, because its location is defined by the CSS style (width = 200px, height = 100px). The div has an id and the CSS defines what the element with that id is.
  4. Next is the script part, which is called from the body when the page loads. The script just defines some global variables and then used querySelector to grab the ball as an object defined by CSS. The ball is ready to be bounced around. Note that "#ball" refers to the div with the id of "ball" that is defined CSS. But the ball is now accessed through "myBall" to give it full JavaScript access. This is that perfect marriage between HTML (the div), CSS style, and JavaScript that means you don't need anything else like Canvas or SVG.

    After the globals and the querySelector, the script contains two functions: playBall and ballMove. The playBall function sets up a game loop that will repeat every 16 milliseconds, which is a magic number that seems to work with most browsers. The loop repeats and each time through, it calls ballMove, which ... moves the ball.

    Actually, the position of the ball is calculated the first time ballMove is called and then the next time it moves the ball. This is weird but seems to work well. The calculation just adds a fixed amount (10 pixels) and then sees if the ball can go there. The ball has a direction it is already going (ballHor and ballVer). The important part is that checks are made to see if the ball will go out of bounds. If it does, the direction will change and the next time through the ball will be moved to a new position. This may seem a bit goofy, but it works. Note that I subtract 10 pixels to account for the amount the ball moves each time it moves.
  5. Finally, the body just simply calls the playBall function when it is loaded.
All this is in less than 100 lines, but you now have something that actually does animation. Note that if you test this in any browser, it will bounce to the edges no matter how big or small, and that this works just fine in Firefox, Chrome, and Internet Explorer. I gave up trying to test Safari, but this should work there. But between you and me, we only care about Firefox!

Let me know if I've not explained things. I'm assuming you know intermediate HTML5, CSS, and JavaScript, but I may have skated past some thing too quickly. Working with CSS is quirky sometimes because you have to reference objects very specifically and you have to keep each one of the three marriage partners (HTML, CSS, and JavaScript) happy.