Showing posts with label #1GAM. Show all posts
Showing posts with label #1GAM. Show all posts

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!