Friday, August 7, 2015

Custom Fonts with Phaser

It's been a while, but I'm back working on my 404-page game (full source code here) built with Phaser. I wanted to add some game text like scores and a title. Fortunately, Phaser makes this fairly simple.

Using the Phaser.Text object, you can render text to the game canvas without much difficulty.

var myScore = 0;
var opScore = 0;

var scoreText1 = game.add.text(20, 10, "Home: " + myScore);
scoreText1.fixedToCamera = true;

var scoreText2 = game.add.text(1060, 10, " Away: " + opScore);
scoreText2.fixedToCamera = true;

The code above creates two text objects that display the team scores, with the text fixed to a certain location on the screen. This means that as the camera moves around, the text won't.

Putting text on a canvas is not trival, but Phaser makes it appear as if it were. To do the magic, Phaser is actually rendering the text to a hidden canvas and using that canvas to get the bitmap image to display in your main game canvas. The catch? Any font you use has to be already loaded and ready in the browser. Not a big deal if you want plain, boring text. But I wanted to use my comic's go-to font Sniglet.

Sniglet is available free from Google Fonts, so I include it on many of my pages the typical way...

<link href='http://fonts.googleapis.com/css?family=Sniglet' rel='stylesheet' type='text/css'>

That doesn't help me in Phaser. Because the font load happens asynchronously, I need to make sure that the font is fully loaded before I try to render any text with it. That's where the Typekit Web Font Loader comes in. Co-developed by Google and Typekit, it gives developers more control over web fonts, and allows me to control the flow of the game setup based on when the font is loaded.

I was a little confused by the Google Fonts integration example provided by Phaser. This is what their example does:

// The Google WebFont Loader will look for this object,
//    so create it before loading the script.
WebFontConfig = {

    //  'active' means all requested fonts have finished loading
    //  We set a 1 second delay before calling 'createText'.
    //  For some reason if we don't the browser cannot render the text the first time it's created.
    active: function() { 
        game.time.events.add(Phaser.Timer.SECOND, createText, this);
    },

    //  The Google Fonts we want to load (specify as many as you like in the array)
    google: {
        families: ["Sniglet"]
    }

};
 
function preload() {

    // Load the Google WebFont Loader script
    game.load.script("webfont", "//ajax.googleapis.com/ajax/libs/webfont/1.4.7/webfont.js");

    // ... load other stuff ...

}

function createText() {

    // ... create text objects here ...

}

I don't like how Phaser's examples just throw the code out there with no explanations. What's going on is a WebFontConfig object is being created for the Web Font Loader to use when it does it's thing. In this example, two fields are specified in the object: active and google. The google field is an object that tells Web Font Loader to load the specified font families from Google Web Fonts. I am only loading one, Sniglet. The active field specifies a function that will be called when the fonts are fully loaded and ready. In Phaser's example, that function waits one second and then calls the createText function. In the game preload, the Web Font Loader script is dynamically injected into the page which kicks off the loading. When it's all done, active gets called, waits one second and calls createText. Text then appears in the game.

But hold on. Look at the comment above active. That's right out of Phaser's example. "For some reason..."???? Comments like that just feed my curiosity. Can I figure it out? Can I make it better?

Of course I tried getting rid of the one-second delay to see what would happen. What happened was that createText was called too fast (before the game's create function), and the text ended up below my ground tiles. The font was fine, but you couldn't see it. Adding the delay back in just ensured that everything else was done before the text was added on top of everything else. So their example works, but that's some weird control flow if you ask me.

I tried taking a slightly different approach. I include the Web Font Loader script explicitly in my HTML and then do the following:

var wfconfig = {

    active: function() { 
        console.log("font loaded");
        init();
    },

    google: {
        families: ['Sniglet']
    }

};

WebFont.load(wfconfig);

// ...

var init = function () {

    // ... variables declared and whatnot ...

    var game = new Phaser.Game(width, height, Phaser.AUTO, "test", {
        preload: preload,
        create: create,
        update: update,
        render: render
    });

    function preload() {
        // ... nothing new ...
    }

    function create() {

        // ... create all the other stuff ...

        scoreText1 = game.add.text(20, 10, "Home: " + myScore);
        scoreText1.fixedToCamera = true;

        // ... and so on, and so on ...

    }

    // ... all the other stuff

}

As soon as the page is ready, I call WebFont.load(wfconfig). Then in the active function, I make the call to set up the game. In my game's preload I don't have to do anything special, and I just create the text objects after all the other game objects to ensure that they are on top (in my game's create function). I shouldn't have to worry about whether or not the font is loaded yet because I put off starting the whole game setup process until after I was sure that it was loaded.

Seemed to work for me. I'm sharing this so that, at least for today, I have not subtracted from the sum of human knowledge. Huh? Read the comic.

Amphibian.com comic for 7 August 2015