Showing posts with label Open Clip Art. Show all posts
Showing posts with label Open Clip Art. Show all posts

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.

Tuesday, November 12, 2013

Touch and SVG Spirals (Game Programming)

I've written a lot about bouncing balls as a way to make the comparison of CSS, Canvas, and SVG standardized. Graphics are about moving things, and that little ball bounces just fine on Firefox OS on my ZTE Open. In my next non-game post, I'll write up a comparison of the three graphics systems with pros and cons. But before I do that, I want to add one more bit about SVG ... and introduce the important issue of touch.

SVG isn't just about simple shapes like balls and boxes. Vector art (formerly known as line art before there were computers) can do incredible things and Adobe Illustrator has a strong place in the graphic design and illustration fields because you can rescale your art to fit a magazine, web page, or whatever. Scalable Vector Graphics is what SVG stands for. SVG does beautiful art by creating paths, which are just a series of lines connecting points.

A Little Bit about SVG

Here's an example of vector art, taken from the Open Clip Art site, a lively spiral created by Martin Bryce.


And here's a small part of the code that created it:

<path
       id="path2987"
       style="color:#000000"
       d="m-176.51-690.97c-59.728-0.23053-121.12 6.8953-145.25 18.438-2.2422 1.0724-4.4748 2.2103-6.6875 3.4375v18.344h-24.312v-1.4375c-13.611 11.42-25.872 25.146-35.688 39.406v22.031h-12.188c-4.0378 11.373-4.9902 23.682-5.625 35.719h16.438c4.2265-15.781 9.1152-32.046 20.281-46.031 19.959-24.998 48.083-49.881 76.781-65.312v-8.6562h19.219c24.836-8.4996 70.064-14.135 116.81-15.562-6.5418-0.21078-13.145-0.34939-19.781-0.375zm19.781 0.375c42.823 1.3798 82.471 6.8046 103.97 17.406v-1.8438h24.312v19.188c15.165 14.106 27.136 32.286 35.687
...
34.366 16.224 77.094 18.531 117.78 18.531 7.1247 0 13.769-0.0951 20.031-0.40625z"
       fill="#f33" />

The code goes on a few pages, and I'm 100% that Martin Bryce didn't sit down with graph paper and plot out those commands. What you're looking at is the definition of a series of points that make up one of the lines. Even though the final result in the image above has lots of wiggly lines, all those wiggles are just a set of points that define the line.

Martin used an art program such as Inkscape or Adobe Illustrator (or maybe something else, but those are the big two). The art program lets you draw with a mouse or use a drawing tablet to make your drawing. Why is this so cool?
  1. You can scale the drawing to be bigger or smaller, or stretch it.
     
  2. When you copy the code into your web page, it takes up less bytes than a bitmap.
  3. And you can get at all of this through JavaScript code and do great mangling!
So that's enough about SVG for a few minutes. It's cool. Check it out. Read about the SVG path command here. Complicated but Inkscape or Illustrator can do all the heavy lifting for you. For Inkscape, check out the book called The Book of Inkscape (very well written by one of the developers) and for Illustrator, track down any copy of any version of the Illustrator Wow! series.

So, go look at those and come back in a minute to ponder the mysterious world of Touch.

Touch

Bouncing a ball around is fine, but if you have a game, you want to interact with the elements on the page. There are a few ways to do that, but the most popular is touch. Touch the screen and make things happen.

For Firefox OS, you can fake touch by using mouse clicks. Mozilla can interpret the mouse code as a touch. No hover, however. Maybe some day, but you can't hover your finger over your phone screen and get any hover.

Code for Touch and Click

So here's the code for responding to a touch on the screen. The touch will move an SVG spiral (simpler than Martin's) to whatever point on the screen you touched, and the x and y coordinates of your touch will be displayed on the screen as well.

<!DOCTYPE HTML>
<html>
   
  <head>
  <title>svg spiral for touch</title>
  <meta charset="UTF-8">
 
  <style>
    #myDiv
    {   
      position:absolute;
      top: 0px;
      left: 0px;
    }
   
    #myPara
    {
      position:absolute;
      top: 50px;
      left: 50px;
    }
  </style>

    <script type="text/javascript">
       
      // Global variables
     
      var screenX = 320;        // Screen x value
      var screenY = 460;        // Screen y value
      var spiralX = 150;        // Spiral x value
      var spiralY = 150;        // Spiral y value

      // Add load event listener to run first function.
      window.addEventListener("load", runFirst, false);
                
      // This function runs when the document loads.
      function runFirst() {

        // Create SVG parent element.
        mySvg = document.createElementNS(
          "http://www.w3.org/2000/svg", "svg");
        mySvg.width.baseVal.valueAsString = "320px";
        mySvg.height.baseVal.valueAsString = "460px";

        // Create SVG screen.
        myScreen = document.createElementNS(
          "http://www.w3.org/2000/svg", "rect");
        myScreen.x.baseVal.value = 0;
        myScreen.y.baseVal.value = 0;
        myScreen.width.baseVal.value = screenX;
        myScreen.height.baseVal.value = screenY;
        myScreen.style.setProperty("stroke-width","1","");
        myScreen.style.setProperty("stroke","red","");
        myScreen.style.setProperty("fill","none","");
               
        // Append SVG parent to div of web page.
        document.getElementById(
        "myDiv").appendChild(mySvg);

        // Append screen to SVG parent element.
        mySvg.appendChild(myScreen);
      
        // Add mouse event listener.
        // Listener is attached to the SVG object.
        // Mousedown is better than click.
        mySvg.addEventListener(
          "mousedown", whereMouse, false);
               
        // Create spiral.
        mySpiral = document.createElementNS(
          "http://www.w3.org/2000/svg", "path");
               
        mySpiral.setAttribute("d",
          "M 8.3052015,10.567223 C "+
          "8.5290778,10.974677 "+
          "7.8664138,11.070027 "+
          "7.6279854,10.93932 6.9818595,10.585111 "+
          "7.1280823,9.668097 7.5610072,9.2127904 "+
          "8.3354083,8.3983538 9.6552076,8.6603337 "+
          "10.33685,9.4509311 11.337187,10.611165 "+
          "10.946898,12.380531 9.7935902,13.276087 "+
          "8.2564118,14.469722 6.0240157,13.946083 "+
          "4.9191211,12.427708 3.5287239,10.516989 "+
          "4.1879167,7.8159392 6.0726185,6.5039261 "+
          "8.3552449,4.9149023 11.527841,5.7107754 "+
          "13.045714,7.9625424 14.834475,10.616172 "+
          "13.901276,14.262005 11.281979,15.984951 "+
          "8.257897,17.974161 4.1377526,16.903234 "+
          "2.2102568,13.916097 0.02011351,10.521923 "+
          "1.2290373,5.9267386 4.5842299,3.7950617 "+
          "8.3482488,1.4036424 13.418989,2.7507484 "+
          "15.754578,6.4741538 c 2.592948,4.1336852 "+
          "1.107525,9.6803612 -2.98421,12.2196622") ;

        mySpiral.style.setProperty("stroke-width","1","");
        mySpiral.style.setProperty("stroke","black","");
        mySpiral.style.setProperty("fill","none","");
       
        mySpiral.setAttribute("transform",
          "translate(" + spiralX + "," + spiralY + ")");
    
        // Append spiral to SVG parent element.
        mySvg.appendChild(mySpiral);
       
        // Initial message.
        myPara.innerHTML = "Touch the screen.";
      }
   
      // Where did the mouse go down?
      function whereMouse(evt){
     
        // Get mouse coordinates.
        mouseX = evt.clientX;
        mouseY = evt.clientY;
       
        // Convert to string for display.
        myCoord = mouseX.toString() + " right, "
          + mouseY.toString() + " down";
        myPara.textContent = myCoord;
       
        // Calculate offset of click to spiral.
        spiralX = mouseX - 8;
        spiralY = mouseY - 8;
       
        // Change spiral position.
        mySpiral.setAttribute("transform",
          "translate(" + spiralX + "," + 

          spiralY + ")");              
      }
             
    </script>
  </head>
   
  <body id="myBody"> 
    <p id="myPara">Text goes here.</p>
    <div id="myDiv" />
  </body>

</html>


The structure follows the same as the last post on SVG. Create an SVG pane, stick an object on it, and then wait for events to happen. There's the usual load event handler, but there is a new one that waits for a mousedown event. Note that I'm using a mousedown event, not a click. For touch, you don't want to wait until the click ends. 

You need to register the mousedown event in the code after you create the SVG object, because the event handler is attached to the SVG pane. This is helpful so that the click goes right to the drawing board.

The mousedown event handler is pretty standard for a mouse event. You get the x and y properties and then you ... apply them to the spiral.

My Spiral

Not as pretty as Martin's, but simpler. Here's the code:

        // Create spiral.
        mySpiral = document.createElementNS(
          "http://www.w3.org/2000/svg", "path");
               
        mySpiral.setAttribute("d",
          "M 8.3052015,10.567223 C "+
          "8.5290778,10.974677 "+
          "7.8664138,11.070027 "+
          "7.6279854,10.93932 6.9818595,10.585111 "+
          "7.1280823,9.668097 7.5610072,9.2127904 "+
          "8.3354083,8.3983538 9.6552076,8.6603337 "+
          "10.33685,9.4509311 11.337187,10.611165 "+
          "10.946898,12.380531 9.7935902,13.276087 "+
          "8.2564118,14.469722 6.0240157,13.946083 "+
          "4.9191211,12.427708 3.5287239,10.516989 "+
          "4.1879167,7.8159392 6.0726185,6.5039261 "+
          "8.3552449,4.9149023 11.527841,5.7107754 "+
          "13.045714,7.9625424 14.834475,10.616172 "+
          "13.901276,14.262005 11.281979,15.984951 "+
          "8.257897,17.974161 4.1377526,16.903234 "+
          "2.2102568,13.916097 0.02011351,10.521923 "+
          "1.2290373,5.9267386 4.5842299,3.7950617 "+
          "8.3482488,1.4036424 13.418989,2.7507484 "+
          "15.754578,6.4741538 c 2.592948,4.1336852 "+
          "1.107525,9.6803612 -2.98421,12.2196622") ;

        mySpiral.style.setProperty("stroke-width","1","");
        mySpiral.style.setProperty("stroke","black","");
        mySpiral.style.setProperty("fill","none","");
       
        mySpiral.setAttribute("transform",
          "translate(" + spiralX + "," + spiralY + ")");
    
        // Append spiral to SVG parent element.
        mySvg.appendChild(mySpiral);
       

This creates an SVG object, and uses a path to define it. There's no fill because this is an open spiral. This spiral uses the SVG transform property to make the spiral translate to a new position. The transform property has a lot of cool options, and its main use is that you can move SVG objects, resize them, or otherwise make them do cool things on the fly with your code. MDN has a good page on transform here

And the last bit is always important. Attach your new SVG object to an SVG object. Don't leave it floating in space.

By the way, I didn't plot out this path either, even though it is simpler than Martin's. I used Inkscape! First I created a 32x32 workspace and then drew a spiral. Inkscape has some built-in spirals (not part of SVG but still cool) so I plopped one down and then saved it (save it as SVG, not as Inkscape SVG) and pasted the code into my web page. Very easy. There's a lot of Inkscape tutorials and other books, but that book by Dmitry Kirsanov is very cool.

So, we've got our spiral, and then when you click on it, the mousedown event handler gets the x and y coordinates and uses the SVG transform/translate attributes to move the spiral to a new position and display the new coordinates.

After the calculation, use this to move the spiral:

       mySpiral.setAttribute("transform",
          "translate(" + spiralX + "," + spiralY + ")");    
          
 

I had also set up a place to put messages with 

        myPara.innerHTML = "Touch the screen.";

And defined the absolute location in the CSS style section. Then to use it, I just put this in the mousedown event handler:

        // Convert to string for display.
        myCoord = mouseX.toString() + " right, "
          + mouseY.toString() + " down";
        myPara.textContent = myCoord;


So after all that, here is what the page looks like on my ZTE Open:


The spiral is near the middle and the message tells you to touch the screen. And when you do, this is what happens:



You can do this all day!

By the way, you want to be careful where you define your objects when working with SVG. In this example, the anchors were in the body. I found that if I put the paragraph after the div, the click wasn't handled if the click was over the paragraph. It's always important to remember that the flow of objects is set by the order in the web page and sometimes that can confuse you when you're throwing about objects.

Next

There's so much to talk about for Firefox OS game programming. Yes, it's HTML5 game programming, but things are different once you're talking about phones. Here's my schedule, subject to change, for the next few non-game-review posts:
  1. Comparison of CSS, Canvas, and SVG
  2. Screen Orientation
  3. Device Orientation (not the same as Screen Orientation)
  4. Collision Detection
  5. A simple first game
And that's just for starters. Stay tuned, but not iTuned.