Tuesday, October 29, 2013

Waiting on font load for HTML5 canvas using Web Font Loader

A very common problem is how to preload a font before running your canvas application. You will easily find out that you have this problem in your hands if text suddenly appears, or starts out as a different font (Flash Of Unstyled Font). One way to fix this issue is to use Google Web Font Loader. There are many tutorials on how to do with with web fonts, but not many with local fonts (your own fonts that you are serving). Here is an example with a simple application:

Let's assume you have some fonts that you want to use for your application, stored under fonts/ directory. Your CSS file fonts.css will look something like this:

@font-face {
    font-family: "digital";
    src: url('fonts/digital-7 (mono).ttf');
}

You will not need to make any changes to that file. Your html file may have looked something like this:

<body onload="myMain()">
    <canvas id="mainCanvas" width="600" height="600"></canvas>
</body>

You will have to change the html file to not load immediately because the issue arises from the browser's asynchronous loading of the font. This means that the font may not be ready when your javascript code finally runs. To call the myMain function only when the fonts load, we will use Google Web Font Loader. This way, the font will be pre-loaded and we can use that font in the canvas.

To do that, all you have to do is remove your call from the body's onload and add it to the activate callback from the WebFontConfig:

<script>
    // load fonts before loading the game
    WebFontConfig = {
        custom: {
            families: ['digital'],
            urls: ['fonts.css']
        },
        active: function() {
            myMain();
        }
    };
</script>
<script src="//ajax.googleapis.com/ajax/libs/webfont/1.4.7/webfont.js"></script>

Make sure to put the two scripts in that order! This is important because you must first create the WebFontConfig object which the webfont.js script will look for. Putting it all together, your html file will look like this:

<!DOCTYPE html>
<html lang="en-us">
  <head>
    <meta charset="utf-8">
    <title>HTML 5 WebApp</title>
    <script src="snake.js"></script>
    <script>
      // load fonts before loading the game
      WebFontConfig = {
        custom: {
          families: ['digital'],
            urls: ['fonts.css']
        },
        active: function() {
          myMain();
        }
      };
    </script>
    <script src="//ajax.googleapis.com/ajax/libs/webfont/1.4.7/webfont.js"></script>
  </head>
  <body>
    <canvas id="mainCanvas" width="600" height="600"></canvas>
  </body>
</html>

Sunday, October 27, 2013

Box2dWeb dynamic objects pass through some static bodies

While making a little game, I ran into an issue when using polygons where some moving objects would collide with a static body and slowly move through it as if it were a dense medium. This issue did not occur when the static body was created with the SetAsBox method. When creating a shape as a box, the moving objects bounced off correctly.

var slantFixtureDef = new b2FixtureDef;
slantFixtureDef.density =     1.0;
slantFixtureDef.friction =    0.5;
slantFixtureDef.restitution = 0.2;
slantFixtureDef.shape = new b2PolygonShape;
slantFixtureDef.shape.SetAsBox(10, 1);

What I really wanted was to use vertices for this shape so that I could rotate it how I wanted.
var points = [
    {x: 0, y: 0},
    {x: 0, y: 0.5},
    {x: 5, y: 0.5},
    {x: 5, y: 0}
];

var vecs = [];
points.forEach(function(e, i, arr) {
    var vec = new b2Vec2(e.x, e.y);
    vecs[i] = vec;
});

slantFixtureDef.shape.SetAsArray(vecs, vecs.length);

But creating a shape like this caused the problem of dynamic bodies not bouncing off this static body. The issue turned out to be that the vertices were specified in the wrong order. Box2dWeb requires you to specify the vertices in clockwise order even though the C++ version requires counter-clockwise order.
var points = [
    {x: 0, y: 0},
    {x: 5, y: 0},
    {x: 5, y: 0.5},
    {x: 0, y: 0.5}
];

Simple as that!

Monday, October 21, 2013

Box2dWeb: b2World is not defined

As I was playing around with a javascript physics engine called Box2dWeb. Almost immediately I ran into an issue. I was receiving the error in Chrome console:

Uncaught ReferenceError: b2World is not defined

This occurs because the objects from this library have a "namespace". Well, actually, there are no namespaces in javascript, but the methods are stored in different objects. To resolve this issue, all you have to do is make aliases for these methods, or in other words, make short local variables for the full methods.

var b2Vec2 = Box2D.Common.Math.b2Vec2,
    b2BodyDef = Box2D.Dynamics.b2BodyDef,
    b2Body = Box2D.Dynamics.b2Body,
    b2FixtureDef = Box2D.Dynamics.b2FixtureDef,
    b2Fixture = Box2D.Dynamics.b2Fixture,
    b2World = Box2D.Dynamics.b2World,
    b2MassData = Box2D.Collision.Shapes.b2MassData,
    b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape,
    b2CircleShape = Box2D.Collision.Shapes.b2CircleShape,
    b2DebugDraw = Box2D.Dynamics.b2DebugDraw;

The author actually recommends this in the documentation. The idea behind the author's suggestion is putting the variables into the scope of the function to improve speed so that the browser doesn't have to search all the scopes.

Tuesday, October 8, 2013

Jetty 9 unsupported version in Eclipse

I ran into an error when trying to run an embedded version version of Jetty 9 in Eclipse. The error was:
Exception in thread "main" java.lang.UnsupportedClassVersionError: org/eclipse/jetty/start/Main : Unsupported major.minor version 51.0
The issue is that Jetty 9 has a dependency on Java 7, but Eclipse was still trying to use Java 6. No matter how hard I tried to edit the execution environment for the project, it would keep using Java 6. The solution for me was to create a new project, and select Java 7 as the execution environment, then copy all the files over. It's a hassle, but it gets around the apparent bug in Eclipse preventing you from changing the execution environment.