Showing posts with label closure. Show all posts
Showing posts with label closure. Show all posts

Wednesday, January 20, 2016

A Match-3 Tile Game in Phaser

It's finally here! The day I release the comic containing my match-3 game! I've been working on this thing for way too long. When I had the idea to put one of those classic tile-matching games in a comic (you know, like Bejeweled) I had thought that it would be easy to put together. I used Phaser, course, but I was unprepared for just how much goes in to one of these things.

Getting this one simple game embedded in the 3rd frame of the comic came in at a couple hundred lines of code. Like everything else in my comic, you can see the complete source code on GitHub (this link goes directly to the game's .js file) so I won't bother to post the whole thing here. But one concept came up more than once during the development which I'd like to talk about.

Tile Matching game with Amphbian.com's frogs

The issue was that I needed to do Phaser Tween animations on possibly over 20 sprites at once, and then take some other action when they had all finished. Making a single Tween call a function when complete is rather straightforward, but what is the proper mechanism for firing off an event when a bunch of other asynchronous events have completed?

Here's an example of a Tween that calls a function when complete:

var tween = game.add.tween(sprite.scale);
tween.onComplete.add(someFunction);
tween.to( { x: 1, y: 1 }, 500,
    Phaser.Easing.Linear.None, true, 0, 0, false);

The above code creates a Tween that will change the x and y fields on sprite.scale both to 1 in 500 milliseconds. When complete, the function someFunction will be called.

Now what if I had 5 Tweens that all take different lengths of time to finish, and I don't want to execute a function until they are all done? Sure, I could just call the function when the longest one is complete, but what if they don't even all start at the same time? It could get messy. The solution is to make a little closure function that counts completions and then executes a callback function when the counter reaches a set level. Here is what I mean...

var num = 5;

var counter = function(total, callback) {
    var t = total;
    var c = 0;
    return {
        inc: function() {
            c++;
            if (c === t) {
                callback();
            }
        }
    };
}(num, someFunction);

var tween = game.add.tween(sprite.scale);
tween.onComplete.add(counter.inc);
tween.to( { x: 1, y: 1 }, 500,
    Phaser.Easing.Linear.None, true, 0, 0, false);

// now make 4 more Tweens here, with different lengths


Assuming I know how many Tweens I'm going to make, which is 5 in my example above, I can declare an anonymous function and then immediately call it passing in that number along with the function I want to use as the callback. That anonymous function creates a closure and returns an object (assigned to the variable counter) with a function inc. Every time counter.inc is called, it increments the internal counter and checks to see if it is equal to the expected total. If so, callback is executed - and here callback is someFunction. The onComplete event on the Tweens just has to call counter.inc and everything goes off like expected. Once all five are done, no matter how long it takes, someFunction will be called.

I ended up using this pattern in two different places in my match-3 game. My game certainly isn't the best example of a classic match-3 tile game but it's also meant to be a joke in a comic. If you're interested in how it works anyway, be sure to check out the source code. At the very least, look at the comic and match up some frogs!

Amphibian.com comic for 20 January 2016

Friday, November 7, 2014

Call Now, Log Later

Sometimes, you really want to cut down trees. But most of the time, logging is just having your software write information to a location where you can examine it at a later time. If you're looking for advice on chainsaws or flannel shirts, I'm sorry but this isn't the blog for you.

I can't tell you much about forestry, but I can tell you about logging from your Node.js application using the Express framework.
Logging, pre-Internet.
AZ Lumber & Timber Co. - 1937

Often, using Express means using the Morgan middleware for logging. Morgan is the HTTP request logger for Express. It allows you to easily output all the typical information you might want to see about your requests and responses. It's easy to use and allows you to customize the format or select one of several standard types.

But what if you want to log differently? I wanted to write the typical HTTP request/response data to a database table so that I could run queries against it later. For testing purposes, I also wanted to be able to optionally output the logs to the screen instead of the database.

I ended up writing my own logger middleware which can use either Morgan (for output to the screen) or a custom function (for output to my database) depending on a flag in my configuration object.

Take a look...

module.exports = function(conf) {

    var myLogger = null;

    if (conf.logToDatabase) {

        // here is where you can set up your database
        // connection or whatever you need

        myLogger = function(req, res, next) {
   
            function storeData() {

                res.removeListener('finish', storeData);
                res.removeListener('close', storeData);

                var reqIp = req.ip;
                var reqVerb = req.method;
                var reqPath = req.originalUrl || req.url;
                var resStatus = res.statusCode;
                var resSize = res.get('content-length');
                var reqRef = req.get('referer');
                var reqAgent = req.get('user-agent');

                // here is where you would call the function to
                // insert your data into the database

            };

            // defer logging until the end so we know the response stuff
            res.on('finish', storeData);
            res.on('close', storeData);   

            next();

        };

    } else {

        var morgan = require("morgan");
        myLogger = morgan("combined");

    }

    return function(req, res, next) {
        myLogger(req, res, next);
    };

};

Based on a check of the logToDatabase field in the supplied conf object (line 5), I decide which logger function to wrap in my returned function. If logToDatabase is defined and true, I create my own. Otherwise, I create an instance of Morgan.

Keep in mind that logging middleware is usually specified first in your Express application. That means the logger function is going to be called before any of your other routes get their chance to process the request. How can the log include things like the response code and content-length if those things haven't been determined yet? The trick to getting the HTTP response data into the logs is found on lines 31 and 32.

Notice that my custom logger doesn't actually do much when it is invoked. It defines a function, storeData, but doesn't actually call it. Instead, I assign the storeData function to be a listener on the "finish" and "close" response events and then just call next(). After that, all the other routes can process the request. Eventually, everybody is done with it and the Express framework ensures that at least one of the "finish" and "close" events gets fired.

It's then that storeData gets invoked. Thanks to the closure, it still has access to the request and response objects - and the response object will be fully populated with data.

On lines 14 and 15, you'll see that I remove the listener from both events. This is because I don't know which of the two events got fired, and it is possible that both will be fired. I don't want to log things twice! Since I can be certain that the function has been called at this point (since it's inside the function!) it is safe to remove both listeners.

Call now, log later.

This is a very useful pattern when developing for Node, not just when logging HTTP request data with Express. Creating function closures and using them to defer execution until an event occurs is a common theme that you'll encounter again and again. Now you'll be prepared.

Amphibian.com comic for 7 November 2014