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

No comments:

Post a Comment