Pages

Thursday, February 27, 2014

Firefox OS Book and What's Next (Game Programming)

I've mentioned this before, but I've been thinking about publishing a book on Amazon covering beginning Firefox OS game programming. As you might guess, the book will consist of roughly 30 blog posts. I'll revise the posts and I'll let you know how I went about publishing a book on Amazon. In keeping with the tone of this blog, my focus will be for people who want to program games but who are not experts and who want to try making things with HTML5 on Firefox OS.

I've already edited about 15 posts/chapters and I expect to edit the rest and publish in about two weeks. Revising for a book takes extra time so I'm planning on cutting back on my posts and catch up on some game reviews.

Here's my tentative list of chapters:

1 -  Introduction - Hello World!
2 -  Pushing your Game to a Device
3 -  Measuring Screen Size from the Inside
4 -  Bouncing a Ball in CSS
5 -  Drawing with Canvas - Part 1
6 -  Drawing with Canvas - Part 2
7 -  Drawing with Canvas - Part 3
8 -  Bouncing SVG - Part 1
9 -  Bouncing SVG - Part 2
10 - Bouncing SVG - Part 3
11 - Touch and more SVG
12 - Screen Orientation
13 - CSS Shell
14 - CSS Collisions
15 - Canvas Collisions in Color
16 - SVG Collisions
17 - CSS, Canvas, SVG Compared
18 - HTML5 Audio - Part 1
19 - HTML5 Audio - Part 2
20 - HTML5 Audio - Part 3
21 - HTML5 Audio - Part 4
22 - HTML5 Audio - Part 5
23 - Vibration
24 - Device Orientation
25 - Local Storage
26 - Asynchronous Storage
27 - IndexedDB Storage
28 - Simple Complete Game - PaddleFox!
29 - Submitting a Game to the Marketplace
30 - Firefox OS Hardware
31 - Resources for Going Further

The code will be retested and will be reformatted so that it fits on Amazon devices (with short line lengths). I'm excited to publish this book and I'm hoping that it will help gain a wider audience for people who want to make games but are intimidated by the more complicated tools of Windows, Android, and Apple development.

What's Next?

After I get the book out, I'm back to writing about game programming. Here are my feelings at the moment:


I've been toying with the idea of writing a space shooter game but also I am seriously drawn to writing an RPG. I've always loved RPG computer games and I've always wanted to write one, but never got around to it. I think this will be what will be next.

I'm going to study Ultima II (my all-time favorite) and also some Gameboy RPG titles like Dragon Warrior, Final Fantasy Legends and Adventure, Ultima Runes of Virtue, Pokemon, and Azure Dreams. These simple tile-based games might just fit on a Firefox OS phone. As with earlier posts, I'll examine what works with CSS, Canvas, and SVG. I won't make a full-blown RPG, but I imagine that there will be a long series of posts. And I'm likely to go with some retro art to go with it.

I'll also keep reviewing games!

So definitely stay tuned, but not iTuned!

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.

Friday, February 21, 2014

Game Jams (Game Programming)

I got so excited by the currently running Flappy Game jam (twitter #flappyjam) organized by Ivano Palmentieri and hosted by Itch.io (http://itch.io). Some game jams are in-person gatherings but many of those allow online participation and then there are jams like Flappy that were organized to be online only.

Flappy Jam was created to nullify some of the bad feelings around the Flappy Bird game (which came out of nowhere and became a mega hit and then was pulled off the store and then had a zillion imitations). The deadline is Monday, February 24, but at last count there were 470 entries and I'm sure the weekend will bring more. The point is to do some kind of difficult to play game that vaguely has something to do with Flappy Bird.

Well, I didn't plan to enter, but I was already intrigued by the idea of Flappy Bird and for the game that I'm writing about now to show off some of the cool things that SVG can do, I decided to enter my game, Flying Fox.


Probably not the greatest game entered (in fact, maybe the worst, if that is possible), but I am happy that I was able to use some SVG tricks to make a game somewhat similar to Flappy Bird. The circles are clouds, the red things are pipes, and the fox has just hit a pipe and blown up. The pipes and clouds are moving from right to left (all at different speeds) and the fox is a nice SVG drawing from Nicu. 

I've written two posts about this game already:



I'll finish the third and final post early next week, but the code was finished and Flappy Jam needed one more entry! Well, probably not, but it is fun to be part of something big and goofy. Check out Flappy Jam at http://itch.io/jam/flappyjam.

If you page through the entries, you'll be amazed at the variety of entries and platforms. I often find variations on a theme fascinating. And if you want to see the original game, well, you can't, but there are some clones in the Firefox OS Marketplace that are very similar. In particular, this one looks just like Flappy Bird: https://marketplace.firefox.com/app/flappy-bird

So my own entry is here: http://itch.io/jam/flappyjam/rate/2878 but you can read about the code next week! 

The other game jam that I've joined in is called One Game A Month and is at http://www.onegameamonth.com/. The idea is that you just do one game a month, any kind, and enter it. There's lots of game information on the site and the founder,  Christer Kaitila, is a cool guy also known as @McFunkypants on Twitter.  My games for @OneGameAMonth (also known as #1GAM) are at http://www.onegameamonth.com/thulfram, but they are just cleaned-up copies of the games I write about here and with any luck I'll be writing at least one game a month and explaining how the code works.

Christer Kaitila also wrote a very cool book about real-life game jams that I highly recommend called The Game Jam Survival Guide which is really useful if you ever go to a live one (and useful even if you don't). Amazon has it at http://www.amazon.com/gp/product/B007R0NTF4?ie=UTF8&camp=213733&creative=393177&creativeASIN=B007R0NTF4&linkCode=shr&tag=animonan-20

So Game Jams are cool and there are jams all over the world, but Game Jellies are coming soon!



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!

Thursday, February 13, 2014

Quarries of Scred (Game Review)

Normally I only review games written for Firefox OS here, but I ran across a game that I think is a type that could be adapted to Firefox OS without a lot of trouble. Games don't have to have elaborate graphics, they just need to be fun to play. Don't wait, get it today, only $3 Australian! http://darkestkale.itch.io/quarries-of-scred, created by the mysterious Darkest Kale!

Quarries of Scred is fun to play but has graphics as primitive as you want. And takes you back into a history of the IBM PC graphics cards.

Do you miss that monochrome green screen?


I remember those screens and what a joy it was when you could finally get color. The first color screens for the IBM PC were CGA and they looked like this:


4 bits for a whopping 16 colors. But it was exciting back then, but we were eager for more!

Next came EGA!


Now you could have 16 colors at a time but chosen from a 64 color palatte with 4 bits, masking, and bit planes. Wow!!!

But we're in modern day and you may want more, so Quarries of Scred offers you 


Super Awesome mode indeed (probably VGA). Those colored balls positively glisten. Beautiful.

Here's what a typical screen looks like:


You are a tiny character at the top, on the left side just to the left of that green building. Along the top are a series of buildings that you can go to that will let you sell your ore, buy supplies, and eventually go home. By the way, in case you couldn't guess, this is a mining game and your job is to travel down into the mine and gather ore but don't get killed.

Here are the rules:


That's it. I could fault this, but this type of game is called a rogue-like because it is modeled after an old game called Rogue where the board is generated new every time you play. Also rogue-like has come to mean that if you die, the game is over, and it wouldn't be fun if they explained too much about the rules.

Your main danger are stones that can fall on you if you're not careful. Not just the stones above you that will fall on you when you remove dirt from between you and the stone above you, but also stones that are not supported on both side.

Some of the shops are fun (they're at the top):

Here's the Bum's Shop. Buy your mines and your mine layer here.


How about the drone shop?


And that all-important Terrence's Gem Refinery?


And who could forget Hiredhelp's Tech shop? You'll need that laser and batteries. Part of the fun is buying stuff and seeing how it works. The game is about getting all the gems and because the layout of each level is different each time, you'll need special tools to get some of the gems that are protected by dangerous stones.


When you finish the level, you leave from the Zep's Telepad!


Cash is easy to get, if you don't die. And you'll die a few times until you figure out some of the ways that those rocks can land on you.

Here's a few unauthorized tips:

1. You can push rocks sideways.

2. Gems aren't a stable place to put rocks on top of.

3. If you remove the wrong thing, a rock will come tumbling down!

4. Watch out! The quarries may not be uninhabited!

But the fun is trying it out and seeing what happens. You can compete for high scores every day with a new seed, so you can show your friends how good you are.

Why did I write about this game? Because the graphics are simple, but the game play is fun, and perfect for a phone. You can play a level in a few minutes. You don't need to do it exactly like Darkest Kale did it, but this game feels similar to Dig Dug and several other mining games. Dig, but don't let the rocks fall on your head.

So look at the game, think of lots of ways to do it differently, and make a game without worrying about graphics. Of course I like the retro look of a game like this, and even the silly Mono/CGA/EGA/VGA screens. But the game play is what this has, and I think it would make a cool phone game.

Thank you, Darkest Kale, for making a cool game!

Cost: $3 Australian
Genre: Mining
Score: 5 (out of 5)
Tested on: PC
Get it at: http://darkestkale.itch.io/quarries-of-scred

ZTE Open Triumphant! (Game Programming)

I used my ZTE Open phone to test out a lot of Firefox OS code and if I hadn't been able to buy it for $79 on Ebay, I wouldn't have started this blog! All along I had wanted to buy a Geeksphone but they were never available when I had the money and when I did, they weren't. Finally Geek and I got together and I got a Geeksphone Peak and have been using that ever since. Then one day, the Firefox OS Simulator said that it wouldn't work any longer with Firefox 26+, so I had to bite the bullet and upgrade my phone if I wanted to use the vastly superior Firefox App Manager.

I had never flashed a phone and had heard all kinds of horror stories about bricking phones, but the stuff I read about upgrading the Geeksphone Peak sounded not too bad, and it wasn't. You can read all about my Geeksphone Peak and upgrading adventure here: http://firefoxosgaming.blogspot.com/2014/02/firefox-os-simulator-rip-game.html. And really, gentle reader, the Firefox App Manager is a lot better. I like how I can see the feedback as the app loads onto the phone, and it also seems a lot, lot faster transferring over the USB cable. I can also take screenshots and there's lot of other debugging things I haven't even discovered yet.

But what about my poor ZTE Open? The horror stories there were long threads of posts with no good conclusion. There was a Mozilla Hacks article that people steered me to, and I tried it, but no help. I even installed Fedora 20 on an old machine but I still got stuck and it seemed too fiddly. I then went to the B2G (Boot to Gecko) mailing list and said what I had done and what was still not working. Well, almost instantly, Ginn Chen came to my rescue with some quick advice. My main problem was that my original ZTE Open didn't have Fastboot installed and that meant it couldn't play ball with Fedora! While the Mozilla Hacks articles were really great, I didn't know how to determine if Fastboot was installed or not, and misunderstood what I had read. The Mozilla team has been very patient with ZTE and me!

I followed Ginn's instructions and combined them with the Mozilla Hacks post by Frédéric Harper and Mark Coggins at https://hacks.mozilla.org/2014/01/upgrading-your-zte-open-to-firefox-1-1-or-1-2-fastboot-enabled/.

I now have this on my ZTE Open:


And now I can use App Manager!


By the way, if App Manager can take screenshots, who takes the screenshot of App Manager in Fedora 20 LXDE? The answer is a cool program called shutter.  Just install it from Yum and you're good to go! 

So what did I do? Follow the instructions from Ginn Chen, Frédéric Harper, and Mark Coggins. I am grateful to them and the rest of the very helpful Firefox OS community. I'm not a hacker, I'm just an ordinary web programmer who likes games and game programming.

Here are the steps:

Step 1 - You Need Fastboot

For some reason, when ZTE started out with the ZTE Open, they didn't know they needed Fastboot. So the first step is to upgrade the OS from 1.0 to 1.1. Get the correct images from here: http://www.ztedevices.com/support/smart_phone/b5a2981a-1714-4ac7-89e1-630e93e220f8.html. Choose the correct image based on where you got the phone. Mine was American eBay. I downloaded it but it took a few tries to get to my machine and took a long time. Test the ZIP file and make sure you can open it. You will be using the zipped version, but the file contains a PDF that might be helpful. 

Next, copy that zip file to the root of a micro-SD card. Take the battery out of your ZTE Open and put the micro-SD card in the ZTE Open. Then put the battery back in (and make sure that no USB cable is connected to a computer. 

Once the battery is back in, press the Volume UP and Power buttons simultaneously. You should see a screen like this:


This is the famous Firefox OS system recovery screen. I can't take this screenshot, but ZTE had this in their instructions. Hopefully you don't get the yellow error messages at the bottom, because you need that card with the update.

Next, using the Volume UP or DOWN buttons, highlight the "apply update from external storage" line. Like this (again from ZTE and not exactly what I had):


Now that you're ready, press the Power button to select the option to apply the update.



Mine didn't have that much and the image loaded very fast. When I saw the message at the bottom saying "Install from sdcard complete" the option was "reboot from system now" and I pressed the Power button to select it. 

My phone rebooted and I was now in phone setup. After the usual questions, everything was fine and was now at Firefox OS 1.1 and Fastboot was my new BFF. 


But I couldn't stop there. I needed to get to 1.2!

Step 2 - Preparing Fedora

I wanted to keep using Windows for my Geeksphone Peak, so it was time to break out an old netbook and install Fedora 20 with the LXDE spin. Someone had recommended this as a Firefox friendly distribution and so far it is fine, but I'm sure other distros like Ubuntu are just fine too. I'm more familiar with Ubuntu and have been a heavy Linux user since I installed Slackware from 3 floppy disks. 

You can get Fedora 20 with the LXDE spin here: http://spins.fedoraproject.org/lxde/#downloads. Essentially Fedora is a version of Red Hat Linux that is community based. But because I had an old netbook, I used a cut-down version called LXDE (Lightweight X-Windows Desktop Environment). My netbook was an old Indamixx brand that was based on the LSI Wind computer with 2GB of RAM. The Indamix is a laptop that has Linux music software preinstalled. I didn't need it for that, so it now is my Firefox build machine!

I downloaded the ISO and used the instructions from this page to install the ISO to a USB stick. My netbook doesn't have a disk drive! http://docs.fedoraproject.org/en-US/Fedora/20/html/Installation_Guide/Making_USB_Media.html.

After installation, I installed Firefox (of course!) and Dropbox (very handy for shuffling stuff back and forth) and Shutter (for screenshots). 

I also installed the latest version of adb (Android Device Bridge) by typing:

   sudo yum install adb

This also installed fastboot.

Then I got the rest of the instructions from https://hacks.mozilla.org/2014/01/upgrading-your-zte-open-to-firefox-1-1-or-1-2-fastboot-enabled/. Which included the instructions on how to prepare my machine from this post: https://hacks.mozilla.org/2013/08/pushing-a-firefox-os-web-app-to-zte-open-phone/. Don't forget to set your phone so that the Developer option for debugging is set!

Plug the phone into a USB cable and then into the Fedora box. Type this:

   adb usb

You should get

   restarting in usb mode

Type 

   adb devices

You should get

   roamer2 device

You're now good to go. Roamer2 is the name for the ZTE Open! Much more romantic.

Step 3 - Load the New Image

Everything is prepared. Get your image here: https://www.dropbox.com/sh/rnj3rja7gd54s98/kGH6LCBRmf.  This is a Dropbox location and again, I chose the US version, not the UK version. These images and the one for 1.1 are just for the ZTE Open phones that were sold for US and UK eBay buyers and may not work for other phones. 

This time unzip the file and put the contents in a folder you can find easily. I made a folder in my home folder and named it zte. Next I plugged in the 1.1 ZTE Open with Remote debugging enabled. 
  • Phone at 1.1 with fastboot and remote debugging enabled - check!
  • ADB and FASTBOOT installed on Fedora - check!
  • 1.2 US image in a folder on my Fedora - check!
  • Fedora "sees" my phone - check!
Open up a command window in Fedora and from the folder that has the new 1.2 images, type:

  adb reboot bootloader

The phone reboots and is now ready for fastbooting! Type these commands, one at a time, while the phone is bootloading:

  fastboot flash boot boot.img
  fastboot flash userdata userdata.img
  fastboot flash system system.img
  fastboot flash recovery recovery.img
  fastboot erase cache
  fastboot reboot


Lots of stuff will happen, and watch for any errors. I didn't see any, but you want to know.

I put those 6 lines in a shell script which made it easier and less worried that I'd make a mistake.

After the last line executes. the phone will reboot and you'll be starting up a brave new world in Firefox OS 1.2.

Installing the App Manager

 This part is the easiest. Just boot up Firefox and select App Manager from the Developer menu. It will gently prompt you to install

   Firefox OS 1.2 Simulator 6.0 ...

And

       ADB Helper 0.2.1

These are Extensions to Firefox. Don't confuse this with Firefox OS Simulator 4.01, which is no longer needed (but was useful to make sure that the device was connected). And the version numbers are only valid for today and may change at any time.

You're in business! Or art or politics or anything you want as long as it is Firefox OS!

One Question

When my ZTE Open was finished booting, it had a bunch of apps on it I didn't install. Anyone know what these are or whether should keep them? They weren't on my Geeksphone Peak when that went to 1.2, and I don't know if I need them or what. Here's some screenshots that show what they look like:


And the second screen.


Well, okay, Flappy Bird and Clumsy Ninja weren't there until I installed them later. But I do wonder what these mystery apps do.

Flappy Bird isn't the original version but is an exact copy from someone else and Clumsy Ninja uses a similar idea to Flappy Bird. Because Flappy Bird has become such a phenomena and has so many clones and even a game jam http://itch.io/jam/flappyjam, I thought this might be a good subject for a simple sample showing some basic moves that you can adapt to something more interesting. My first post on this, Flying Fox, is here: http://firefoxosgaming.blogspot.com/2014/02/flying-fox-part-1-game-programming.html.

Again, thanks to the Firefox OS community and in particular to Ginn Chen, Frédéric Harper, and Mark Coggins, for making the ZTE Open now a proud member of the Firefox OS 1.2 community.