Showing posts with label SVG. Show all posts
Showing posts with label SVG. Show all posts

Monday, June 23, 2014

News Flash! Firefox Has In-Browser IDE (Game Programming)

By an interesting coincidence, I've been helping a friend set up the Android Development environment and it is a pain! But not FirefoxOS! It is really easy to develop for FirefoxOS ... but it just got a lot easier!

I've suffered through all kinds of hideous development environments, and most of them are a pain because they 're designed for developers who supposedly are willing to take any kind of abuse. "Are you a masochist? (pause) Well, good, then you'll be right at home with Windows, Android, Apple, and so on."

Firefox OS was great because you only had to edit file in your text editor and then upload them to the browser (with a few plugins). Well, today is history. The latest Nightly build came out and it has an "Integrated Development Environment." What that means is that once you have Nightly, you're done! No more plugins, no more text editor. I'll miss you, Notepad++!

So, here's what you are really, really going to want to do and never look back.

Install Nightly

You already are doing this, right? Just install Nightly and have it update itself every night. Get it at http://nightly.mozilla.org/. You know you want to!

Enable the New App Manager

Once you have the latest Nightly installed, type this in the search window:

about:config

No spaces before or after the colon. You'll get a warning.



Ignore it. You're living on the cutting edge, right? Maybe some of the settings could break your windows, but this just will allow the new IDE to come to life.

Once you promise to be careful, you'll see a giant list of settings that you can change. Scroll down until you see


Yes, I know that webide sounds like something spiders clean their houses with, but it really is WebIDE (Web Integrated Development Environment). Right click on

devtools.webide.enabled

and when a little menu comes up, select true. Nothing happens right away, but the magic is about to happen.

Now go to your menu and select Developer and then App Manager. Yes, this is still called App Manager but the name may change to WebIDE.

And the App Manager has changed ... a lot!

Meet the New App Manager, Not the Same as the old App Manager

Here it is:


It looks simple but it is a lot more powerful than before.

The first thing I did was to open one of my packaged apps. How? Maybe it's the little button on the left that invites me to Open App? Yes!


I have choices and I'm choosing Open Packaged App. I navigate to my app and load it. Without any delay at all, my app is loaded and you can see it. I'm using the simulator here but the same is true with an actual phone. Here's my app:


Cool already. It shows me the files in my app on the left and information about my app on the right. This is a simple app I'm rewriting right now to convert it to SVG and incorporate the viewport, so I can see how big an app really is. More about that soon. You'll notice that it is a VALID app by the green button on the right.

The next thing I want to do it run it, so I go to the View menu and choose Manage Simulators. Why? Well, you get a choice of simulators for the different versions that are supported.


This shows you what is installed and what is not. I had the ADB Addon Helper and the OS 1.3 Simulator already installed, but by clicking on the install buttons for the later versions, you can see them too. You'll notice that 1.0, 1.1, and 1.2 are no longer supported.

So pick the OS version of your choice


Select a runtime. I selected 1.3 since that was all I had at the moment. It will also show your device if you have it hooked up. Once you've done that, just click on the arrow in the middle to start your app.

Amazing! As expected, it opens up a separate simulator window and you can see your app running. Here's my app, the one that shows you how large your device (or simulated device) is.


So far, not that special. But it now gets cooler because you can load up other environments. Here I am loading up Firefox OS 2.0. I didn't even know they were that far ahead!


Let's see what version 1.4 looks like. (Actually I know because that's what is running on the tablet. But I haven't seen it on a phone-sized window.


It looks different. This is part of the new school of flat icons. Easier to understand. I like the radio!

Does my app run in 1.4?


It looks the same, but the height is slightly different. The outer height is slightly larger because there was a file menu that is now gone. But the inner is still the same.

Okay, so this is slightly nicer than the old App Manager, but what's the fuss?

This!


If I select one of the files on the left, a text editor pops up. I picked the index.html file because that's where the meat is. Notice that the text is colored nicely.

I can see my icons even though I can't edit them.


Again, only maybe nicer. But it gets better!

From the project menu, you can select New App and make it all right there in the browser.


You'll be asked what kind of app you want. There will be more choices as time goes on, I'm sure. And here's your new app, ready to be filled with code-delicious stuff.


This is now starting to rock. Now Mozilla is giving you a template with icons, a manifest, and javascript and css files, showing you where everything should go. Thank you, Mozilla!!!

I noticed something really cool that I hadn't hear of before. One of the icons is an SVG file! You can click on it and see that yes, Virginia, it is an SVG file. No art here, just fabulous SVG code.


This needs to be investigated. Maybe you don't need anything but one SVG icon and you're done?

And if you run this new app template, it really does run!


I'm expecting a set of steak knives with the next release!

But I've saved the best for last. You can edit your code in the IDE and see the changes immediately. No more going back to your editor and reloading umpteen times.

Open the debugger in the App Manager and see the code:


I've highlighted a line of code that sets the body color. Before it was using the color "Peru" but I changed it to"Blue" and here is what I got.


On the fly! This is really astounding. Write your code, see your code, make changes in the debugger, and never leave your browser again. Thank you, Mozilla. This will cut everyone's development time down a lot, so now you won't have an excuse not to turn out tons of cool apps tomorrow!

This really is now the coolest development environment in the universe.

The only way it could be cooler is to be able to do this on an actual phone or tablet. Or even in Firefox for Android. Imagine if you could code in your tablet. I've seen several browser IDE apps for Chrome OS, so why not here? But I'm not complaining. This is a giant step forward in programming. Wow!

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.

Wednesday, February 19, 2014

SVG and Z-Order (Game Programming)

Actually this is Part 2 of Flying Fox, my soon-to-be-entry in the #FlappyJam thingee and my next for #1GAM. But even more important, some further work on using SVG in games.

Often when designing a game, you need to decide what draws on top of what. In other words, if you think of a game as a set of moving objects in layers, you need to know what is in the layer closest to the viewer and what is farthest away. Often this just works out to be what is in the background and what is in the foreground. For HTML5 games written in Canvas, everything depends on what order you blast the bits to the canvas.

But for SVG, your objects exist in layers. In fact, each object has its own layer. But how do you determine which layer is closer and which layer is farther? In CSS, you have the concept of z-order, and you can change the layers dynamically. If you're doing a lot of changing which order the layers display, CSS might even be a good choice. But I'm investigating SVG at the moment. I would have expected SVG to have a z-order property because so much of SVG leverages CSS. Well, there isn't one. You can't just do something like:

  objectName.style.zOrder = 10;

The sad truth is that there isn't any such thing and if something is planned, it's not here now. The z-order of your objects is strictly controlled by the order your objects are defined. In the code I have for you today, the object will be defined in this order:

  1. SVG object
  2. background object
  3. cloud
  4. another cloud
  5. flying fox
I'm adding in a few clouds. Why? To give the illusion that the flying fox is flying. The clouds are whizzing by at different speeds, and the fox isn't really moving but we want to pretend. Refer back to the first post on Flying Fox at http://firefoxosgaming.blogspot.com/2014/02/flying-fox-part-1-game-programming.html.

Here's what the screen will look like with today's code:


 The fox (courtesy of Nicu) is flying and the green and blue clouds (circles) are floating as he flies. The clouds move from right to left. And here's what it looks like when the fox flies in front of the green cloud:



And, again, when the fox flies in front of the blue cloud:


I'm sure you can do prettier clouds yourself, especially if you draw them in Inkscape and then import them into your game (as shown in Part 1 of Flying Fox).

Here's the complete code:

<!DOCTYPE HTML>
<html>
<head>
  <meta charset="utf-8">
  <title>
    Floppy Fox Part 2
  </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;

  // 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 an SVG element.
    gameBoard =
      document.createElementNS(svgURL, "svg");

    // Width and height of the 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 the 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);
  
    // Start the game loop.
    gameLoop();
  }

  // Game loop.
  function gameLoop(){

    // Endless loop with requestAnimationFrame.
    requestAnimationFrame(gameLoop);

    // The fox waits to drop.
    // Only you can save him.
    foxWait();
   
    // Move the clouds.
    moveClouds();
  }
 
  // 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; 

      // Alert and reset.
      alert("Fox hit the floor!");
      console.log("Fox hit the floor!");
      return;
    }

    // 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; 
     
      // Alert and reset.
      alert("Fox hit the roof!");
      console.log("Fox hit the roof!");
      return;
    }
   
    // Otherwise, draw the fox.
    myFox.y.baseVal.valueAsString =
      foxY + "px";
   
    // Report progress.
    console.log("Fox is jumping.");  
  }

  </script>
  
</head>

<body id="pageBody">
</body>

</html>


The only new parts of the code are:
  1. Two new globals
  2. Two clouds
  3. Call from the game loop to draw clouds
  4. The function that draws the clouds

New Globals

I added two globals to keep track of the x-coordinate of the clouds. Since they only move from right to left, you only need to change one value for each to move the cloud.

// Cloud initial x coordinate value.
  var cloud1X = boardWidth;
  var cloud2X = boardWidth / 2;



These are defined by their relationship to the initial board width, in numerical values (not pixels, but they will be converted to pixels to draw using them).

Cloud Objects

The clouds are just SVG circles.

    // 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);


You can read more about circles in the post on SVG Bouncing Balls Part 3 at http://firefoxosgaming.blogspot.com/2013/11/bouncing-svg-part-3-game-programming.html.   The cx value (x coordinate for the center) is defined by the global cloud1X or cloud2X and then must have the "px" added to it to convert it from a number to a pixel number. SVG is finicky about the kind of number it wants. And always make sure that once you define an object with its coordinates, shape, size, and color, that you add it to an existing SVG object that is connected to the main SVG object.

Game Loop

You need a single call to the cloud drawing function:

    moveClouds();

This will be called every time that requestAnimationFrame runs the loop, and this is where the animation changes take place: the bird flies based on your tapping the screen and now the clouds move every time the loop runs.

Moving the Clouds

The clouds move with function:

  // 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";     
  }


First a number is subtracted from the current cloud x-coordinate. You can use decimals to have it go slower.

Next you check to see if the cloud went off the left edge. You want to check to make sure it's all the way off the screen, so check to see if the position is at -100, which is twice the radius of 50. If it is, then reposition the cloud to equal the width of the screen (boardWidth) and it will be redrawn the next time through the loop. Notice that the second cloud is subtracting 1 instead of 0.5, so that it will move faster. This kind of motion (called parallax) gives the illusion of distance, because clouds further away give the appearance of moving at a different speed.

How lovely it is to be a cloud!


Next time: we've got a flying fox and moving clouds, but it's only pretty to look at, and not a game. We need to add obstacles that the fox can try not to crash into. Or maybe a game review. Who knows? Stay tuned but not iTuned!

Tuesday, February 11, 2014

Flying Fox Part 1 (Game Programming)

Okay, I'm interested in Flappy Bird, but not because of the success and drama. What interests me is that it is a one-finger game. This works for touch and I think has something to look at. So I'm now going to create a similar game that just uses a finger to move the hero forward.

But I've been intrigued by this ever since I reviewed Captain Rogers. You could say that Captain Rogers is the same as Flappy Bird, in that you are flying through something and avoiding something and that the mechanism is the same. At first I didn't like the game because it was too hard, and I don't like Flappy Bird either. But the mechanic is interesting, so I'm going to create a game that uses touch and moves an object through an always-scrolling maze. But I need something that flies. How about a fox?


This is an image created by Nicu Buculei of Romania, who has been doing cool SVG tutorials for years. This image grew out of a tutorial called Pac-Man baddies with Inkscape and is available on the OpenClipart.org site (where I get all my clip art from). I really love SVG and this game will be done in SVG, using our fox-like friend, who can magically fly.

In earlier SVG game posts, I've just concentrated on shapes like squares and circles, but it's time to take wings and fly. But how to use a beautiful work of art in SVG? You can see the source code for this work of art at http://openclipart.org/detail/19560/game-baddie:-fox-by-nicubunu and I won't paste it in here because it takes up several hundred lines. Now Nicu didn't plot out the art by hand, he used Inkscape, which makes the details of the SVG art hidden, and all you need to do is use the art.

The game will let you keep the fox flying by tapping on the screen. Here's a screen shot of what it looks like on my Geeksphone Peak (now running in FxOS 1.2):



Tap and he flied upward. Don't do anything, and the fox falls! In later posts I'll show how to scroll and how to draw in the obstacles, but being able to import an SVG file is enough for now.
Here is the code for Part 1.

<!DOCTYPE HTML>
<html>
<head>
  <meta charset="utf-8">
  <title>
    Flying Fox Part 1
  </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;

  // 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 an SVG element.
    gameBoard =
      document.createElementNS(svgURL, "svg");

    // Width and height of the 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 the game board.
    gameBoard.appendChild(gameBack);

    // 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);
   
    // Start the game loop.
    gameLoop();
  }

  // Game loop.
  function gameLoop(){

    // Endless loop with requestAnimationFrame.
    requestAnimationFrame(gameLoop);

    // The fox waits to drop.
    // Only you can save him.
    foxWait();
  }

  // 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; 

      // Alert and reset.
      alert("Fox hit the floor!");
      console.log("Fox hit the floor!");
      return;
    }

    // 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; 
     
      // Alert and reset.
      alert("Fox hit the roof!");
      console.log("Fox hit the roof!");
      return;
    }
   
    // Otherwise, draw the fox.
    myFox.y.baseVal.valueAsString =
      foxY + "px";
   
    // Report progress.
    console.log("Fox is jumping.");  
  }

  </script>
  
</head>

<body id="pageBody">
</body>

</html>


A lot of this code is similar to other SVG posts I've written, and here's a brief outline.

Shell

Uses the standard HTML5 shell for SVG. For a detailed explanation, see this post: http://firefoxosgaming.blogspot.com/2013/11/bouncing-svg-part-3-game-programming.html.
I have some globals, I set up an event listener, and then start running the game when the page loads.

Game Loop

The game runs in a game loop, using requestAnimationFrame for the basic display timing.

Action

Every time through the game loop, the program goes to a waiting routine that waits one second. After the second has passed, the fox falls a small amount. If the fox hits the ground, the game is reset.

Touch

But to keep the fox from falling, you can tap the screen. I use the mousedown event in much the same way I did in the post Touch and SVG Spirals. Tap on the screen, trap the tap with an event handler, and then move the fox up. But be careful. If the mouse hits the roof, the game is also reset.

SVG

The real meat of this is importing the SVG image. Here's the code that does this:

    // 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);


This code is in three steps:

1. Create an SVG element called myFox using the SVG image element. Define the height, width, x, and y values to give it a location and size. foxX and foxY will determine where the fox is placed.

2. Then we must import the fox, which is in a separate file called fox.svg. We do this by using a magic technology called XLINK. One of the tricks to working with SVG is to remember that under the hood, SVG is XML, not HTML. Even though HTML5 has graciously allowed SVG to come and live in their house, SVG is still an XML animal. And XLINK is a linking protocol that was designed for XML. To use XLINK, you must introduce a separate namespace. SVG has one and now XLINK does too. Up in the globals, I defined svgLink as the namespace for XLINK, so you must use that when you invoke it, because it isn't part of HTML5 or SVG. But it works just fine. You import it by adding the href attribute to the SVG object myFox and then supply the URL, which in this case is just the filename, fox.svg. Complicated, but you only need to learn it once.

3. Then, once you've created the SVG image and imported the art into the image, you just need to add the image to the main SVG object, gameBoard. The image is now part of the SVG drawing and you can manipulate it all you want through JavaScript.

The fox is now ready to fly. Load this into your phone, watch the fox fall, and then tap to keep him from falling to the floor or hitting the roof.

Background

I had one problem with this that I needed to fix. In my last post when I was raving about how cool App Manager was, I showed a preliminary screen shot of the game I'm writing about now.


You'll notice that the top and left edges show a white margin. That's not what I wanted, and it made me dig out my old code from my post on screen sizes: http://firefoxosgaming.blogspot.com/2013/10/how-big-am-i-game-programming.html. Turns out that I hadn't measured the screen with my code and the new code showed this:


My inner screen size was now 360 x 620 pixels. So I added an SVG rectangle that I could color just to show a completely solid background, helpful to see where the fox is. Here's the code I used:

    // 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 the game board.
    gameBoard.appendChild(gameBack);


Standard stuff, just a rectangle. Define it, attach it, color it "Thistle", and you're done.

But when I loaded it into my phone, it still had that damn margin. I check and all browsers give you a margin when you throw an SVG object at them.

But when things don't work, there is the cousin of SVG, known as CSS! By simply giving the SVG board an ID, I could then place it exactly where I want it to go with some simple CSS. I understand browser manufacturers wanting to give a little margin (I don't think this was always true), but I want the game to go right to the edge.

So I added this CSS and everything fit perfectly:

  <style>
        
    #mySVG
    {
      width: 360px;
      height: 620px;
      position: absolute;
      top: 0px;
      left: 0px;
    }
     
  </style>


How cool is that? The mixture of HTML5, CSS, JavaScript, and SVG is really rocking. And thank you to Nicu for sharing with the world how to draw SVG and make it look good.

But it also makes me see that you have to be sure of what your screen is. I'm thinking I might clean up my "How Big?" app and putting it in the Marketplace for other programmers. We will be having more phones, tablets, and devices.

Next up, scrolling the screen.

Wednesday, January 15, 2014

PaddleFox (Game Programming)

One of the simplest games to create is a variation of Pong that is based on the real-life sport of Racquetball. You can play by yourself and don't need a partner. Just slam the ball against a court made of three walls and bat it back and forth. I wanted to be more unique to choose a name for this game, so after some thought and research, I picked PaddleFox.

Why pick a name? Part of this series of posts is to create real games, and right now seems like a good time to pull together a lot of the technologies I've been exploring to make a "real" game and see how to put it in the store. This isn't going to be the greatest game in the marketplace and I certainly wouldn't charge money for it. I might expand on it later. There's an absolutely fabulous game I do love that is on the Android called Juggle by those wizards of Dundee, Scotland, known as Denki. I recommend it and I'd like to try something like it as I build up more skill. (Essentially it is similar to the game I'm building today, but introduces more balls and different sized ones.)

I'll see about submitting this game to the Firefox OS Marketplace and keep you posted. As I improve it over time, I'll let you know about that too. But I also have lots more game techniques I want to explore that aren't about bouncing balls, and can't wait to get going on a space shooter, a platform game, and a lite RPG.

But on to today's game. There's a ball, there's a paddle, and that's it. Hit the ball and it bounces off. Miss the ball and the game is over (but you don't need a quarter to play over again). This game uses SVG, but you can do the same game by using the corresponding bouncing ball and collision techniques I wrote about for CSS and Canvas.

Here is ... PaddleFox!


Tested and run on a Geeksphone Peak, my new pride and joy. I feel guilty not playing with my ZTE Open. I know it is sitting in the corner missing me, but I'm hoping to get back to it when I can safely upgrade to Firefox OS 1.3 so I can start using the App Manager.

Basically this game combines the bouncing ball technology of creating SVG with JavaScript and the SVG DOM with the Collision Detection built into SVG with getBBox with a touch of Touch! SVG is still a largely undiscovered country for game programming (but see a good one in esviji).

So, here's the code:

<!DOCTYPE HTML>
<html>
  <head>
    <meta charset="utf-8">
    <title>
      PaddleFox
    </title>
   
    <style>
   
    #myHead
    {
      position:absolute;
      top: 10px;
      left: 10px;
    }
   
    #myPara
   
    {
      position:absolute;
      top: 60px;
      left: 10px;
    }
   
    </style>
    
    <script>

      // --- Global variables ---

      // Width and height of board in pixels
      var boardWidth = 320;
      var boardHeight = 460;
      var boardWidthPixels = boardWidth + "px";
      var boardHeightPixels = boardHeight + "px";
     
      var padMove = 20;  // Amount the paddle moves.
      var score = 0;     // Initial score.

      // URL for W3C definition of SVG elements
      var svgURL = "http://www.w3.org/2000/svg";
          
      // Variables for ball initial position.
      var ballHor = boardWidth / 2 - 20;
      var ballVer = 50;
      var ballHorPixels = ballHor + "px";
      var ballVerPixels = ballVer + "px";
     
      // Amount of vertical and horizontal change.
      // Think of this is a vector. Speed + direction.
      var changeHor = 5;
      var changeVer = 5;
     
        // Covers all versions of requestAnimationFrame.
        window.requestAnimationFrame =
        window.requestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.msRequestAnimationFrame; 
     
      // Page load event listener.
      window.addEventListener("load",
        runFirst, false);
       
      // Mousedown event listener.
      window.addEventListener(
        "mousedown", whereMouse, false);
                       
      // Runs when page loads.    
      function runFirst() { 

        // Define the game board as an SVG element.
        gameBoard =
          document.createElementNS(svgURL, "svg");
         
        // Width and height of the SVG board element.
        gameBoard.width.baseVal.valueAsString =
          boardWidthPixels;
        gameBoard.height.baseVal.valueAsString =
          boardHeightPixels;
         
        // You must append the board to the body.
        document.getElementById("pageBody").
          appendChild(gameBoard);
       
        // Define the ball as an SVG element.
        paddleBall =
          document.createElementNS(svgURL, "circle");
         
        // Width,  height, radius, and color of ball.
        paddleBall.cx.baseVal.valueAsString =
          ballHorPixels;
        paddleBall.cy.baseVal.valueAsString =
          ballVerPixels;
        paddleBall.r.baseVal.valueAsString =
          "10px";
        paddleBall.style.
          setProperty("fill","fuchsia","");
         
        // Attach the ball to the game board.
        gameBoard.appendChild(paddleBall);
       
        // Define the paddle.
        myPaddle =
          document.createElementNS(svgURL, "rect");
         
        // X, Y, width, height, and color of paddle.
        myPaddle.x.baseVal.valueAsString = "90px";
        myPaddle.y.baseVal.valueAsString = "400px";
        myPaddle.width.baseVal.valueAsString = "130px";
        myPaddle.height.baseVal.valueAsString = "20px";
        myPaddle.style.setProperty("fill", "darkcyan","");
       
        // Attach the paddle to the board.
        gameBoard.appendChild(myPaddle);
       
        // Color the page elements.
        pageBody.style.backgroundColor = "black";
        myHead.style.color = "red";
        myPara.style.color = "white";      
        
            // Start the game loop.
            gameLoop();
      }
       
      // Game loop. Runs as fast as it can.
    function gameLoop(){
     
            // Endless loop with requestAnimationFrame.
            requestAnimationFrame(gameLoop);
      
        // Move the ball.
        ballMove();        
    }
     
    // Move the ball.
    function ballMove() {
     
      // Changes are calculated but do not
      // take effect until next time through loop.
         
      // Calculate new vertical component.
      ballVer = ballVer + changeVer;

      // If top is hit, bounce off.
      if (ballVer + changeVer < 10)
        changeVer = -changeVer;
       
      // If bottom is hit, the game is over.
      if (ballVer + changeVer + 10 > boardHeight) {
        changeVer = -changeVer;
        // You missed!
        console.log("Hit bottom");
       
        // Game over - actions
        // Set the score to zero.
        score = 0;
        myPara.textContent = "Score = " + score;
       
        // Alert the player.
        alert("You missed the ball!  Start again");
       
        // Start over again.
        // Set ball position.
        ballHor = boardWidth / 2 - 20;
        ballVer = 50;
       
        // Set ball vector.
        changeHor = 5;
        changeVer = 5;
       
        // Set paddle position.
        myPaddle.setAttribute("x", "90px");
       
        // Exit here and don't do any more now.
        return;
      }

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

      // If left edge hit, bounce off.
      if (ballHor + changeHor < 10)
        changeHor = -changeHor;
       
      // If right edge is hit, bounce off.
      if (ballHor + changeHor + 10 > boardWidth)
        changeHor = -changeHor;
       
      // Does a collision take place with paddle?    
      // Get bounding box of ball.
      bbBall = paddleBall.getBBox();      
      // Get bounding box of paddle.
      bbPaddle = myPaddle.getBBox();        
     
      // Compare bounding boxes.   
      // Is the ball intersecting the paddle?
      // Four conditions must be met.
      if  ((bbBall.x > bbPaddle.x) &&
        (bbBall.x < (bbPaddle.x + 140)) &&
        (bbBall.y > bbPaddle.y) &&
        (bbBall.y < bbPaddle.y + 10)){
          console.log("HIT"); 
         
          // If hitting the paddle, reverse vertical.
          changeVer = -changeVer;
         
          // Make a small spin to create variation.
          spin = Math.floor((Math.random()*5)) - 2;
          // Add spin to horizontal vector.
          changeHor = changeHor + spin; 
         
          // Bounce ball up to untangle from paddle.
          ballVer = ballVer - 20;
         
          // Increase score.
          score = score + 1;
          myPara.textContent = "Score = " + score;
      }         

      // Draw the ball with new coordinates.
      paddleBall.cx.baseVal.valueAsString =
        ballHor + "px";
      paddleBall.cy.baseVal.valueAsString =
        ballVer + "px"; 
    }

    // Handle mousedown event.
    // Tap once each time to move paddle.
    function whereMouse(evt) {
   
      // Get mouse coordinates.
      mouseX = evt.clientX;
      mouseY = evt.clientY;
     
      // Current paddle position.
      oldX = myPaddle.x.baseVal.value;   
     
      // Calculate which side click is on.
      // If tap is on right of center.
      if (mouseX > 160) {
        newX = oldX + padMove;
        // If new x would go off screen.
        if (newX > 210)
          newX = oldX;
      }
      // If tap is on left side of center.
      else {
        newX = oldX - padMove;
        // If new x would go off screen.
        if (newX < 10)
          newX = oldX;
      }
     
      // Move paddle.
      myPaddle.setAttribute("x", newX);     
    }   

  </script>
</head>
   
<body id="pageBody">
<h1 id="myHead">PaddleFox</h1>
  <p id="myPara">Score = 0</p>
</body>

</html>


268 lines of code. That's not too many! Here's how it works.

Shell

I use a standard HTML5 shell. The minimum you need to tell the browser that you're talkin' HTML5 and UTF-8. Here's the layout I use, but all this is up to you. The HTML5 part is not. Even though it is not directly about SVG, my post on CSS Shell is a good starter.

In general, here's how my shell is laid out. You can also call this a template, but I like shells, and Susie sells them down by the seashore!
  1. HTML5 minimum code
  2. CSS
  3. Globals
  4. Event listeners
  5. Page Load event handler
  6. Game Loop
  7. Function blocks
  8. Other event handlers
  9. HTML UI code (if any)
The HTML5 and Globals should be easy enough to figure out. I use globals when I want something to be accessible in any event handler.

CSS

Sometime I use CSS and sometimes I don't. When you are using SVG (which has cousins in CSS on its mother's side), you can avoid CSS, but it's always there in the background, cooking up delicious meals. Here's today's CSS:

    <style>
   
    #myHead
    {
      position:absolute;
      top: 10px;
      left: 10px;
    }
   
    #myPara
   
    {
      position:absolute;
      top: 60px;
      left: 10px;
    }
   
    </style>


I use the CSS here to position the heading and paragraph. If you don't absolutely position these text elements, the SVG pane will be drawn after these, but this will make it go off the bottom of the screen! When doing games, you need to make sure that everything is absolutely positioned.  Just to be different, I colored these text elements in code a little later:

        pageBody.style.backgroundColor = "black";
        myHead.style.color = "red";
        myPara.style.color = "white";      


I did this in the page load. I'm actually more comforable with JavaScript than CSS, but I'm learning CSS fast.

Event Listeners

Games are usually all about events, and you need to listen for them. Here are the listeners:

       window.addEventListener("load",
        runFirst, false);
      
      // Mousedown event listener.
      window.addEventListener(
        "mousedown", whereMouse, false);

The first is listening for the page load event and the second is looking for a mouse down event. On Firefox OS, the mouse down event is what I use for simple touch. Your finger is a mouse! You want just the mouse down, not the click (which is mouse down plus mouse up). Fast and Firefoxy!

Page Load Event Handler

I throw in everything here that should happen before you start running the game. This includes:
  1. SVG definitions for the SVG pane, the ball (a circle), and the paddle (a rect). This is explained in the SVG post about SVG with JavaScript and the SVG DOM with the Collision Detection built into SVG with getBBox
     
  2. Whatever else I want to set up (coloring the text, in this case).
     
  3. A simple call to the game loop. Game loops govern the action cycle in an arcade game and they've been around forever. 
Game Loop

This is a simple game loop. It just loops around and around, governed by requestAnimationFrame. All it does is draw the ball (ballMove).

Function Block

The only function I have is (that isn't an event handler or the game loop) is the one that moves the ball. This function, ballMove, does most of the heavy lifting. It does the following things:
  1. Calculate the new ball position based on the changes you made the last time through the loop. This is a little goofy, but works. The loop is always processing what happened last time through. For example, if you hit a ball, you bounce, but the actual bounce doesn't happen until you come back again. Fortunately the game loop is bringing you back several times a second, depending on the negotiation between requestAnimationFrame and your browser. First the vertical component of your ball's vector (direction of travel and speed) is calculated, later the horizontal component follows.

    ballVer = ballVer + changeVer;
  2. A check is made to see if you hit the top of the screen. If you did, you bounce!

          if (ballVer + changeVer < 10)
            changeVer = -changeVer;

     
  3. Next a similar check is made to see if you hit the bottom of the screen. Oh, oh! You don't want to do that. If you did, the following actions take place: your score is reset to zero, the player is alerted, the ball is reset to its original position, so is the paddle, and then you return to the game loop without drawing the ball in this function block.

          if (ballVer + changeVer + 10 > boardHeight) {
            changeVer = -changeVer;

            console.log("Hit bottom");
           
            score = 0;
            myPara.textContent = "Score = " + score;
           
            alert("You missed the ball!  Start again");
           
            ballHor = boardWidth / 2 - 20;
            ballVer = 50;
           
            changeHor = 5;
            changeVer = 5;
           
            myPaddle.setAttribute("x", "90px");
           
            return;
  4. Next you do the same for horizontal positions. Bounce off the left and right walls.

          ballHor = ballHor + changeHor;

          if (ballHor + changeHor < 10)
            changeHor = -changeHor;
           
          if (ballHor + changeHor + 10 > boardWidth)
            changeHor = -changeHor;
  5. Now its time to see if the ball hits the paddle or not. This is a smart ball and does all the thinking. The paddle just moves (but elsewhere). This is a simple process. Get the bounding boxes of the ball and paddle. Even though the ball is round, a box can be used to determine collisions. Then once you get the bounding boxes, use their data to check a rectangle that sits on top of the box. If the ball enters that rectangle, a collision takes place. That collision will then make the ball reverse its vertical vector component, add a small random change to the horizontal vector component (called spin), pop the ball up a bit so it doesn't stay tangled in the paddle, and adds +1 to your score.

          bbBall = paddleBall.getBBox();      
          // Get bounding box of paddle.
          bbPaddle = myPaddle.getBBox();        
         
          // Compare bounding boxes.   
          // Is the ball intersecting the paddle?
          // Four conditions must be met.
          if  ((bbBall.x > bbPaddle.x) &&
            (bbBall.x < (bbPaddle.x + 140)) &&
            (bbBall.y > bbPaddle.y) &&
            (bbBall.y < bbPaddle.y + 10)){
              console.log("HIT"); 
             
              // If hitting the paddle, reverse vertical.
              changeVer = -changeVer;
             
              // Make a small spin to create variation.
              spin = Math.floor((Math.random()*5)) - 2;
              // Add spin to horizontal vector.
              changeHor = changeHor + spin; 
             
              // Bounce ball up to untangle from paddle.
              ballVer = ballVer - 20;
             
              // Increase score.
              score = score + 1;
              myPara.textContent = "Score = " + score;


    The only tricky part is the actual collision detection.

                if ((bbBall.x > bbPaddle.x) &&
            (bbBall.x < (bbPaddle.x + 140)) &&
            (bbBall.y > bbPaddle.y) &&
            (bbBall.y < bbPaddle.y + 10)){


    This checks four conditions to make sure that the ball's bounding box is inside the paddle's bounding box and uses the && to say that all four must happen to trigger the collision.
  6. Finally if you didn't hit the bottom of the screen, the ball is drawn.

          paddleBall.cx.baseVal.valueAsString =
            ballHor + "px";
          paddleBall.cy.baseVal.valueAsString =
            ballVer + "px"; 
This is the only trick part, but it seems to work well. And unlike Canvas, you don't have to erase anything, you just move stuff. Well, CSS game programming can just throw stuff around, but only SVG can do really cool shape modification and filters (more about that later, lots more).

Mousedown Event Handler

 This is triggered any time you touch the screen. In order to make this a game, you have to do something. My thought is to make it simple, so you touch on the left half of the screen to move the paddle left, and touch on the right half to make it go right. Each time you tap the screen, the paddle will jump a specific number of pixels and you'll have to tap it again to move it again. This provides a small amount of challenge.

Interestingly enough, when testing this on desktop Firefox, the clicking to move is clumsy and slow. But on a phone, it's fast and very intuitive. No keyboard, no mouse, just your finger. Tap anywhere on the left to move left, etc.

The mousedown handler does the following:
  1. Get the coordinates of where the tap took place.
  2. Get the current paddle X position (we're only moving left and right).
  3. Calculate where the click took place. Center is at 160 pixels right of the left edge. If the tap is on the right, add padMove to the paddle X value. But check to make sure the paddle doesn't move off the right edge.
  4. Do the same for the left of center tap. Again, make sure you don't move the paddle off the left edge.
  5. Move the paddle!
Here's the code for the mousedown handler:

      mouseX = evt.clientX;
      mouseY = evt.clientY;
     
      oldX = myPaddle.x.baseVal.value;   

      if (mouseX > 160) {
        newX = oldX + padMove;

        if (newX > 210)
          newX = oldX;
      }

      else {
        newX = oldX - padMove;

        if (newX < 10)
          newX = oldX;
      }
     
      myPaddle.setAttribute("x", newX);     


Wrapping It Up

You just start the game and move the paddle, stopping the ball from its mad passion to slam into the bottom of the screen. Rack up your score and keep playing. Maybe even have fun. But the real point is to see how to pull together the elements of creation, motion, and collision to make a simple arcade game.

Next I'm going to try to put this in the Marketplace and relate my adventures. After that, more game programming, more game reviews, and so on until I run out of ideas (not soon). Any ideas you have or any simple game types you'd like me to create would be appreciated. Well, maybe not Call of Duty.

Written while listening to the soothing strains of the Panzer Dragoon Saga Memorial Edition soundtrack. One of my all-time favorite games, totally only on the Sega Saturn. I don't have it any longer but I still remember finishing it and really liking the world it took place in, one of archaeology and dragons.