Wednesday, November 6, 2013

Bouncing SVG - Part 1 (Game Programming)

Earlier in this blog I wrote about bouncing a ball in CSS and several posts about bouncing a ball with Canvas. But there's a third web technology that might be perfect for some types of games, and its name is SVG. You may have heard about it, you may know that SVG stands for Structured Vector Graphics, but you probably don't know more than that, because the story of SVG is one of starts and stops, success and failure, and yes, slowly but surely, SVG keeps marching on. I won't bore you with the whole story, because this set of three posts is all about the three ways to use SVG to bounce balls so you can make choices about the graphic technology you want to use to make games for Firefox OS phones.

In the late 90's, the world was discovering the web. But the web was slow, especially when it came to graphics and pictures. Why not use vector graphics and describe them mathematically? Just a few lines and you have a fast-loading picture. Microsoft came up with one system (VML) and Adobe came up with another (PGML). Some others became interested and a new standard was invented that took parts of both and combined them to make SVG. It was a cool dream, but it didn't quite come together.

Microsoft didn't want to support SVG and they stuck with VML but didn't really promote it as a graphics language. The main use for VML was to represent Office Art for Office 2000, but not much else. Adobe promoted SVG but it was a constant uphill battle because without Microsoft support, nothing much was going to happen. VML was forgotten, and even though there were a dozen books about SVG, nothing much happened. 

One of the main reasons for the sidelining of SVG was that the Internet was speeding up fast and graphics now loaded just fine, thank you. It was a bitmap world!

Until Tim Bernars-Lee wrote a prominent article that called attention to the fact that Microsoft wasn't supporting important standards like SVG. Anyone else might have been ignored, but Tim was the founder of the Internet, so Microsoft decided to hire some new folks and make Internet Explorer support web standards (they'd shut down the team and sent them over to DirectX, allowing Firefox and Chrome to take up the slack).

So SVG was now implemented in most browsers, but the story doesn't end there, at least for game programming. Most everyone who knew about SVG looked at it as a lightweight way to have vector drawings. Professional illustrators had been doing this for years (with ... Illustrator). But as a static art format, SVG wasn't all that interesting.

The final thing that made SVG really useful was that when HTML5 was being created, some bright person decided it might be cool to have SVG be supported as if it were HTML. This was really cool because before that, SVG was seen only as an XML format. Nice, but a pain to work with. 

But by being able to treat SVG as a HTML, you could code that would put SVG shapes, lines, and paths in your web page. For example, the code below will create a red circle in a web page using HTML5.

<!DOCTYPE html>
<head>
</head>
<body>
<h3>This is SVG</h3>
<svg width="120" height="120" >
<circle cx="60" cy="60" r="50" fill="red" />
</svg>
</body>
</html>
And here is what it looks like:

All that I did was to add the SVG element, define its width and height, and then inside the element, I defined a red circle with a center of 60,60 and a radius of 50 pixels. SVG is still XML underneath, but you can treat it as any other element, and SVG has a lot of things it can do: shapes, paths, shading, text. Everything is defined by a markup language, and now it fits right in with HTML like they were siblings (they both have the parent of XML).

And you might ask yourself, how well is SVG supported, now that Internet Explorer supports it (and who cares about them anyway)? Chrome 4 and Firefox 2 were the first supporters, but right now, according to that wonderful site, Can I Use?, all the major browsers support SVG.(Opera Mini was the last holdout). And of course, Firefox, Firefox OS, and Firefox for Android support it too. 

But, if you look closely at the Can I Use? statistics, it says 84% support. The reason for this is that the SVG specification http://www.w3.org/TR/SVG/ is an extremely complicated document and has a lot nooks and crannies that aren't supported by anyone and may never be. The spec is still at version 1.1 (Second Edition) and change is slow. Parts that are not likely to be complete include typography and animation. But 84% of a huge spec is just fine (and the other 16% you don't care about unless you are a mad typographer.

The point here is that SVG is now in every browser, it does graphics with vectors, and its now much easier to use because you can treat it like any other HTML elements. It's right there for the taking! But because of the checkered history, people got excited about SVG (a dozen books!), but then lost interest when Microsoft wouldn't play along. And the Internet got fast, so no one cared anyway.

So why bother? SVG isn't just a pretty face. It has hidden depths, very deep depths. Here's the secret:

SVG CAN MOVE!

SVG can be moved, shaken, stirred, and in general, messed about with. Every element and attribute can be manipulated with JavaScript. So make your game bits and move them around. Because there are no bitmaps, you can have very lightweight graphics, and lightweight is cool on a Firefox OS phone. And Firefox has a pretty extensive set of reference pages on SVG at https://developer.mozilla.org/en-US/docs/Web/SVG. Of course!

Let's move something. How about a bouncing ball? 

But before I start throwing code (and fuchsia balls) at you (and after all that boring Internet history), I need to explain why there are three basic ways to use SVG in HTML5.
  1. The easiest way is to build your SVG art in the body of the browser. I did this just a few paragraphs ago by making a red circle. But the red circle can be manipulated by JavaScript. You can change the size, color, and position very easily. You can stretch it, mangle it, whatever you want. But unlike Canvas, SVG doesn't leave anything behind when it changes. The new size, color, position are preserved in the web page memory. None of this annoying draw, move, erase old, repeat. Just draw and move!
     
  2. The next way is to create all your SVG element and attributes on the fly with JavaScript. Want a circle? Make it in JavaScript. This all of a sudden becomes programming instead of markup language. You create elements, you modify them, and you do it all in JavaScript. You do this by using the HTML DOM (Document Object Model). You create SVG objects, define their attributes, and it is magic! More about this with the next programming post.
     
  3. But there's more. SVG has its own DOM. When you programmed SVG with method #2 (and actually #1 for the JavaScript part), you were talking to the SVG DOM through the HTML DOM.It turns out there is a way to go directly to the SVG DOM, and bypass the HTML DOM. This is faster and you can do even more manipulation. It's definitely tricky,but gives you greater persormance and control. And if you know how to read the SVG specification, all the answers are there. More about all this DOM stuff in the second programming post after this (sandwiched in with game reviews). 
Today's post will cover the first technique. Create an SVG object in the body with SVG tags, and then manipulate it with JavaScript. I'll use a lot of the same structure I used for CSS and Canvas, and the look and feel of the output will be the same, and, like all the other code I've put in this blog, it has been tested on Firefox OS on the ZTE Open phone.

Here's the code:

<!DOCTYPE HTML>
<html>
  <head>
    <meta charset="utf-8">
    <title>
      SVG in the body
    </title>
   
    <script>
   
      // Global variables
     
      var boardWidth = 320;
      var boardHeight = 460;     
      var ballHor = boardWidth / 2;
      var ballVer = boardHeight /2;
      var changeHor = 10;
      var changeVer = 10;
     
      // Covers all bases for various browser support.
      var requestAnimationFrame =
      window.requestAnimationFrame ||
      window.mozRequestAnimationFrame ||
      window.webkitRequestAnimationFrame ||
      window.msRequestAnimationFrame; 


      // This function is called on page load.
      function drawGameSVG() {
       
        // The page is loaded.
        console.log("The page is loaded.");   

        // Start the game loop.
        gameLoop();
      }
     
      // Game loop.
      function gameLoop() {
     
        // Move the ball once each loop.
        ballMove();
     
        // Restart the loop with requestAnimationFrame.
        requestAnimationFrame(gameLoop);   
      }

       // Move the ball.   
      function ballMove() {
     
        // Changes are calculated but do not
        // take effect until next time through loop.
       
        // Set the SVG attributes of the ball.
        ball.setAttribute("cx", ballHor);
        ball.setAttribute("cy", ballVer);
         
        // Calculate new vertical component.
         ballVer = ballVer + changeVer;

        // If top is hit, change direction.
        if (ballVer + changeVer < -10)
          changeVer = -changeVer;
       
        // If bottom is hit, reverse direction.
        if (ballVer + changeVer > boardHeight - 10)
          changeVer = -changeVer;

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

        // If left edge hit, reverse direction.
        if (ballHor + changeHor < -10)
          changeHor = -changeHor;
       
        // If right edge is hit, do something.
        if (ballHor + changeHor > boardWidth - 10)
          changeHor = -changeHor;   
      }

     </script>
  </head>
 
  <body onload="drawGameSVG()">

    <!-- Create the SVG board. -->
    <svg width="320" height="460" id="board" >
      <!-- Create the ball. -->
      <circle cx="150" cy="150" r="10" fill="Fuchsia" id="ball" />
     
    </svg>
  </body>

</html>


The body of the page does two things:
  1. Call the drawGameSVG function when the page loads.
  2. Creates an SVG container with the id of "board" and a ball with the idea of "ball". You need to define these with an id so JavaScript can grab them later. SVG always needs a container to start with. The ball is the only visible aspect and the properties of position (cx,cy), radius (r), fill (fuchsia), and id (ball) are needed to have a complete ball.
The JavaScript is nearly the same as the Canvas code. It starts the game loop, uses requestAnimationFrame to keep the loop going, but uses some new commands to actually move the ball once the new position has been calculated:

        // Set the SVG attributes of the ball.
        ball.setAttribute("cx", ballHor);
        ball.setAttribute("cy", ballVer);


When the ball was defined in the body with SVG tags, it had the id of "ball". That id is used here, and the attributes of the ball are modified in the function called ballMove. The method setAttribute is used to change the definition of the "cx" and "cy" positions to match whatever the new ball position is (as defined by ballHor and ballVer). ball is an object, and because HTML5 treats SVG elements as HTML elements, you can change any attribute by using setAttribute.

So, that's it. Not so hard, really. You can check out the MDN SVG pages, and especially look at the tons of samples they have at https://developer.mozilla.org/en-US/docs/Web/SVG/Element. SVG is huge, but I'll try to cover it as I go along, because there's a lot there that can be of value to game developers using HTML5.

Also it might be good to say that if you're not patient enough to do elaborate drawings in SVG by hand coding every twist and turn, you don't have to. There are two very excellent drawing programs that support SVG extremely well. The first is Inkscape, which is a free open source program that is just astounding. I'll talk more about Inkscape later, but it is worth checking out, and is 100% based on SVG. The other SVG art program is Adobe Illustrator, which is not free, but is also now supporting SVG pretty well. But my heart is with the wonderful folks at http://inkscape.org/ I love their motto: "Draw freely."

As with this post, and any others, if I didn't express something well or you have questions, make a comment and I'll try to answer it. This game programming stuff is complicated, especially with all the tons of technologies available free in Firefox OS!

No comments:

Post a Comment