[Phaser] XML / bitmap font workaround in Cocoon


#1

XML / bitmap font workaround in Cocoon

This tutorial is made by Michel (Starnut) in HTML5 Game Devs forums.

Original source


Step by step quick guide

  1. Create the bitmap font with any tool that provides XML based BMFont output. Personally I used bmGlyph for Mac, but it should work fine with Littera (free web service) or GlyphDesigner as well. Angel Code’s Bitmap Font Generator for Windows.

  2. I use the modified DOMish parser suggested by @Hsaka in my index.html right before I create the Phaser game instance.

  3. I pick the file-format within the preloader depending on whether it runs under CocoonJS or not and attach it to the file names like so:

var fileFormat = (this.game.device.cocoonJS) ? '.json' : '.xml';
this.load.bitmapFont('titleFont', 'assets/fonts/title-font.png', 'assets/fonts/title-font' + fileFormat);
  1. I rename the font data files from .fnt to .xml and convert them using a modified version of @Hadan’s nodejs script (you need nodejs installed for this). I improved it so it converts all .xml files within a given folder to .json files. The command line call now looks like this (assuming it lies at the top level of your project):
    $ node fontConverter.js assets/fonts/

If you don’t pass a folder, it converts all files within the folder the script lies in.
Moreover, you can now pass it a string to filter for, if you don’t want all of the files in the given folder to be converted. The following call converts all XML files in the folder assets/fonts/ that have ‘title’ in their names:

    $ node fontConverter.js assets/fonts/ title

And here is the above mentioned script from the fontConverter.js file:

    var fs = require('fs'),
    xml2js = require('xml2js');
    
    // init the file parser
    var parser = new xml2js.Parser({ explicitRoot: false, mergeAttrs: true, explicitArray: false });
    // init the target directory
    var targetDir = __dirname +  "/";
    // check if a target directory was passed
    targetDir += (process.argv.length > 2) ? process.argv[2] : '';
    // check if a file filter was passed
    var fileFilter = (process.argv.length > 3) ?process.argv[3] : null;
    
    console.log('\nLooking for files in', targetDir);
    
    if (fileFilter) console.log("... the names of which contain the string '"+fileFilter+"'\n");
    
    var parseNextFile = function(files){
        
        if (files && files.length > 0)
        {
            // get the first file in the list
            var file = files.shift();
            // split off pure file name without format ending
            var fileName = file.substr(0, file.indexOf("."));
            // read the file
            fs.readFile(targetDir + file, function(err, data)
            {
                // parse the file's content
                parser.parseString(data, function(err, result)
                {
                    // keep only the relevant information
                    result.char = result.chars.char;
                    delete result.chars;
                    // turn it into a JSON string and write it into to a .json file with the same name as the XML
                    fs.writeFile(targetDir + fileName + ".json", JSON.stringify(result), function(err) {
                        if (err) 
                        {
                            console.log(err);
                        }
                        else
                        {
                            console.log("Converted file", file, "to", fileName + ".json");
                            // parse the next file
                            parseNextFile(files);
                        }
                    });
                }); 
            });
        }
        else
        {
            console.log("Conversion complete...\n");
        }
    };
    
    
    // check if the target directory exists
    if (!fs.existsSync(targetDir))
    {
        console.log(targetDir, "does not exist!");
    }
    else
    {
        // read all files from the target directory
        var files = fs.readdirSync(targetDir);
    
        var n = files.length,
            i;
        for (i = n; --i >= 0;)
        {
            var file = files[i];
            var index = file.indexOf(".xml");
            // only process XML files that match the given filter
            if (index === -1 || (fileFilter && file.indexOf(fileFilter) === -1))
            {
                // delete the file entry
                files.splice(i, 1);
            }
        }
        if (!files || files.length === 0)
        {
            console.log("No files found!");
        }
        else
        {
            // parse all found files
            parseNextFile(files);
        }
    }

It’s a mix of synchronous and asynchronous calls, cause I didn’t manage to let it all asynchronously and I’m a little short on time. Not perfect but it does the trick.