Monday, February 15, 2016

Switching to Frame Based Tweens in Phaser

This weekend I was doing some more work on my 8-bit style Phaser platformer. I added another enemy (a rabbit) and made some changes which will make it easier to add even more enemies. But during my extensive play-testing I noticed that I was having a lot of trouble with my moving platforms. Both the ridable clouds and the collapsing platforms seemed to move away from the frog, and then the frog's motion became very choppy as it was repositioned.

Frogs can now battle rabbits in the dark forest.

After playing around with some things, I determined that the issue was caused by a slightly lagging frame rate. I had a bunch of other things running on my laptop and the game wasn't running quite as quickly as it was before. The physics engine, which controlled the frog's movements, was making updates based on how much time was elapsing between frames. The movement of the platforms was being handled by Tweens, which by default calculate their movement in constant time. When the frame rate dips below 60 per second, the two get out of sync. Normally this would not be that noticeable but in the situation where the two objects are tied together, it caused some issues.

To resolve these problems, I switched the Tweens into "frame based" mode. This is as easy as setting a boolean flag once in the create function.

function create() {
        
    // ... other stuff ...
     
    this.tweens.frameBased = true;

    // ... other stuff ...

}

When running in frame based mode, the Tweens will use the physics engine's update timer. When I switched to this mode, the frog and the platforms remain in sync much better when the frame rate dips a little. I did have to adjust the duration values in most of my Tweens, since in frame based mode the duration should represent number of frames instead of number of milliseconds. But it was a simple adjustment.

Remember, you can view the complete source code for the game on GitHub, and play the current version at this link: http://amphibian.com/eight-bit.

Before you go, be sure to read today's Amphibian.com comic - and give me a vote on TopWebcomics.com if you have an extra second!

Amphibian.com comic for 15 February 2016

Friday, February 12, 2016

Happy Valentine's Day

I also love frogs.
With Valentine's Day only 2 days from now, I thought I'd take this opportunity to talk about love. What do I know about love? I know that the English language expresses it very poorly.

For example,

I love my wife.
I love my children.
I love pizza.

Those three sentences use the same word, but all refer to very different kinds of love. Why does our language have so little precision when it comes to such an important concept? I have no idea. I just use English; I didn't invent it.

Facebook is famous for its Like button, but it has no Love button. Twitter just replaced Favorite with Like, but Like is represented by a heart. On Facebook, Like is a thumbs-up. Does that mean Twitter likes are more passionate? Does anyone else besides me wonder about these things?

Today's comic has a special feature - there are two hidden frames at the end which will become visible once the comic has at least 50 likes on Facebook. In mathematical terms, 50 likes = 1 love. Who says I can't mix logic and emotion? Vulcans, I guess.

Amphibian.com comic for 12 February 2016

Wednesday, February 10, 2016

Don't Fall Through - "Tile Bias" in Phaser

Despite getting a lot of good level design accomplished this weekend, and adding collapsing platforms to my Phaser platformer, my free time since Sunday has been extremely limited. I haven't been able to work on much else, so today's post will be short.

One thing I would like to address is the use of "tile bias" in Phaser. In one of my levels, I had an issue where if the frog jumped from a platform near the top of the screen he would fall through the ground instead of colliding with it. It wouldn't happen 100% of the time, but it was often enough. I'd seen this type of problem before, back when I wrote my own HTML5 game engines instead of using Phaser. It is a classic problem - when there is too much time lapsing between frames (for whatever reason - slow computer, too many background tasks, etc.) the physics engine can miss collisions. A fast-moving sprite can traverse such a great distance in the time between frame updates that its bounding box ends up completely on the other side of an object with which it should have collided.

When it comes to a sprite colliding with a tilemap in Phaser, the collision calculation can be tuned by changing the value of TILE_BIAS. This was exactly what I needed to do because the issue was the frog hitting (or in this case, not hitting) a ground tile.

The default value for TILE_BIAS appears to be 16. This means that for a 32x32 tile, there's a 48x48 box when it comes to collision checks. It doesn't mean that a colliding sprite is moved back an extra 16 pixels to get it out of collision, the value is only used for the collision check. So don't worry - a sprite can still appear to be right next to a tile! I tried changing the value from 16 to 32 and that solved the problem for me. If you have a similar issue in your game, you'll probably have to play around with the number to find what works in your situation.

I change the value in my create function, at the same time I set up the other physics stuff.

function create() {
        
    game.physics.startSystem(Phaser.Physics.ARCADE);
    game.physics.arcade.checkCollision.down = false;
    game.physics.arcade.TILE_BIAS = 32;
    game.physics.arcade.gravity.y = 1500;

    // ... other stuff ...

}

Remember, if you want to view the complete source code for the game it is available on GitHub. And remember to view today's comic. Both comics so far this week have dealt with lunch. Maybe I'm hungry when I'm writing these.

Amphibian.com comic for 10 February 2016

Monday, February 8, 2016

Collapsing Platforms in Phaser

I've been working on level design over the last few days for my Phaser platformer (view the complete source code on GitHub). I used my floating, ride-able cloud platforms (that I wrote about last week) in a few places, but I also found a few places where I wanted to use a collapsing platform. You know, one that falls after you stand on it for a little while.

The red-and-black platforms will fall after you jump on them.

To start, they are very similar to the riding platforms. The difference is in the custom collision callback function, shown below.

function fallPlatformSep(s, platform) {

    if (!s.locked) {
        s.lockTo(platform);
    }

    if (!platform.activated) {

        platform.activated = true;

        // save the original Y-value
        var origY = platform.position.y;

        platform.game.time.events.add(1000, function() {

            var t = platform.game.add.tween(platform.position);
            var dist = ((platform.game.world.height + 100) - platform.y);
            var time = dist * 2.25;
            t.to( { y: platform.position.y + dist }, time, Phaser.Easing.Quadratic.In, false, 0, 0, false);
            t.onComplete.add(function() {

                // after falling, wait 2 sec. before putting it back
                platform.game.time.events.add(2000, function() {
                 platform.activated = false;
                 platform.position.y = origY;
                 var t2 = platform.game.add.tween(platform);
                 t2.to({ alpha: 0.5 }, 100, Phaser.Easing.Linear.None, true, 0, 3, true);
                });

            });

            t.start();

        }, this);
        
    }
    
}

function update() {

    // ... other stuff ...

    this.physics.arcade.collide(frog, fallingPlatforms, fallPlatformSep, null, this);

    // ... other stuff ...

}

Since this function is called when the frog and the falling platform are colliding, the first step is to "activate" the platform, if it has not been activated already. This block should be entered the first time that the frog lands on the platform and won't be entered again no matter how long he stands on it.

The next step is to make a note of the original Y-value, so it can be put back a few seconds after it falls off the screen. The drop and restoration is handled by a few game timers and tweens. The first timer sets it up so that the platform will drop 1000 milliseconds after the first contact. Inside the timer function, I set up a Tween to move the platform down to a final position of 100 pixels below the world bounds. This makes sure that if the frog rides it to the bottom he will be complete off-world and trigger the kill function.

I'm using the Phaser.Easing.Quadratic.In easing function in the drop so that it gets off to a slow start. I actually found a very useful page with live demos of all the Phaser easing functions: just click here!

When the drop Tween completes, I set a second timer. Two seconds after the fall, the platform gets set back to its original Y-position and once again becomes inactive. Just to make it look nice, I use another Tween here to blink the alpha a few times.

But don't let your interest in my comics drop as fast as these platforms! Check out today's comic before you go to other parts of the Internet, or order lunch, or whatever.

Amphibian.com comic for 8 February 2016

Friday, February 5, 2016

Use Tiled's Custom Properties in Phaser

To make editing the maps for my Phaser platformer easier, I decided to make use of the custom properties Tiled lets you add to objects. These get directly translated to fields on the Sprite objects if you use the createFromObjects function to pull them from the map.

I used this technique on my moving cloud platforms. In the map editor, I select the object and then add two custom properties: direction and distance.

Adding two custom properties in Tiled

Once I saved my map, I could then use those properties when I set up my moving cloud platforms. In the create function, I make the platforms from the objects on the map. Look at the following code.

function create() {

    // ... other stuff ...

    platforms = this.add.group();
    platforms.enableBody = true;
    map.createFromObjects('others', 6573, 'cloud-blue', 0, true, false, platforms);
    platforms.forEach(function(p) {
        
        this.physics.enable(p, Phaser.Physics.ARCADE);
        p.body.allowGravity = false;
        p.body.immovable = true;
        
        var t = this.add.tween(p.position);

        // use the distance and direction custom properties
        // to set up the Tween
        var dist = (p.distance ? Number(p.distance) : 128);
        if (p.direction == "left") {
            t.to( { x: p.position.x - dist }, 3000, Phaser.Easing.Linear.None, true, 0, -1, true);
        } else if (p.direction == "right") {
            t.to( { x: p.position.x + dist }, 3000, Phaser.Easing.Linear.None, true, 0, -1, true);
        } else if (p.direction == "up") {
            t.to( { y: p.position.y - dist }, 3000, Phaser.Easing.Linear.None, true, 0, -1, true);
        } else if (p.direction == "down") {
            t.to( { y: p.position.y + dist }, 3000, Phaser.Easing.Linear.None, true, 0, -1, true);
        }
        
    }, this);

    // ... other stuff ...

}

As I iterate over them to perform their setup, I can read p.distance and p.direction in order to customize the movement Tween. I first check to see if distance is defined and use a default value if it is not. This way, the game doesn't fail if I forget to set the custom property. I then check the value of the direction field and set the Tween accordingly. If I forget to set direction on one of my objects, it just won't move because the to function will never be called on the Tween.

I like this method because it allows me to quickly and easily tweak the level in the map editor until I get the level design right. I don't have to go back to change any code when I want to move a platform's starting position, direction, or travel distance. I might add a third property set set the speed as well.

Remember, you can see the complete source code for the game on GitHub!

While you're here, why don't you take a few minutes and catch up on some comics that you may have missed - like today's! It deals with the first and best troubleshooting technique there is.

Amphibian.com comic for 5 February 2016

Wednesday, February 3, 2016

Ride Moving Platforms in a Phaser Game

Another staple of classic platformers that my Phaser game has been missing is the moving platform. You know, those floating platforms that go up and down or side to side and upon which you can ride? I had to add some. It wasn't that hard, but there were some tricks to it.

This is the frog's new cloud platform.

First, I had to add the sprites for the moving platforms. I did this by adding them to my map in Tiled as objects, the same way I added the enemies (see that blog post here).

platforms = game.add.group();
platforms.enableBody = true;
map.createFromObjects('others', 6573, 'cloud-blue', 0, true, false, platforms);
platforms.forEach(function(p) {
    
    game.physics.enable(p, Phaser.Physics.ARCADE);
    p.body.allowGravity = false;
    p.body.immovable = true;
    
    var t = this.add.tween(p.position);
    t.to({ y: p.position.y - 200 }, 3000, Phaser.Easing.Linear.None, true, 0, -1, true);
    
}, this);

Every object with id 6573 was assigned the "cloud-blue" sprite image key and put in the platforms group. Then I iterate over the group to set them all up with physics so they can collide with the frog. I turn off gravity on them so they don't fall off the screen, and I set body.immovable to true. This isn't to say that they can't move - it just means that they won't react to being pushed down when the frog lands on them. The movement of the platforms is controlled by Tweens. In the above code snippet, all of the platforms are given the same Tween. This one example Tween will move the platform up 200 pixels from its start position in 3 seconds and then loop forever. In my finished game I'll use different Tweens for different platforms.

After that, adding a collision check in the update function between the frog and the platforms group was all it took to make the platforms solid.

I now had moving platforms upon which the frog could land, but the behavior wasn't correct. As the platform moved up it would push the frog up with it, but on the way down the frog would keep falling and hitting the platform over and over again. It didn't look like he was riding it. The solution was to create a special collision callback function which "locks" the frog to the platform upon which he stands.

function platformSep(s, platform) {

    if (!s.locked) {
        s.locked = true;
        s.lockedTo = platform;
        s.body.velocity.y = 0;
    }

}

function update() {

    this.physics.arcade.collide(frog, platforms, platformSep, null, this);

    // ... other stuff ...

    if (frog.locked) {
        if (frog.body.right < frog.lockedTo.body.x || frog.body.x > frog.lockedTo.body.right) {
            frog.locked = false;
            frog.lockedTo = null;
        } else {
            frog.x += frog.lockedTo.deltaX;
            frog.y += frog.lockedTo.deltaY;
        }
    }

    // ... other stuff ...

}

Now, when the frog and a platform are in collision, the frog will get a locked flag set to true, and the lockedTo field will be set to the platform sprite on which he stands. The frog's y-velocity is also set to 0 here to stop any potential falling that might occur otherwise.

Once I had these additional fields, I could perform a check to see if the frog is currently locked to a platform. If he is, but his current position says that he's walked off of it, I cancel the lock and his behavior goes back to normal. Otherwise, the frog's position gets updated to match the movement deltas of the platform. This keeps him riding it, no matter which in direction it moves.

These moving platforms really add a lot of classic platformer feel to the game, and thanks to Phaser they weren't too difficult to add. Remember, you can see the complete source code to the game on GitHub and play the current version at this link!

There are some major upgrades going on in the comic today, so don't forget to read it!

Amphibian.com comic for 3 February 2016

Monday, February 1, 2016

Generating Color Gradients with JavaScript

The comic frames with the color
gradient backgrounds.
For the comic on Friday, I had to generate a color gradient between the full-brightness daylight color in the third frame and the nighttime darkness color in the fourth frame. Since the point of the comic was to adjust the brightness using a slider with 20 positions, I needed 40 colors (including the 2 starting colors) so that when the brightness was all the way down both frames would have the same background. They would meet in the middle.

It was actually a very nice comic. On the right you can see the frames with the color gradients, but the real comic is interactive - go play around with it if you haven't yet!

To generate the gradient of colors, I found a set of JavaScript functions which I will share here. I can't take credit for it, all I did was rearrange some of the code and re-order the output (the original seemed backwards to me) but it is very nice. I am keeping it in the source code for the Amphibian.com comic editor in case I ever need it again.

Here is the function. Calling it should be self-explanatory. Note that the colorCount parameter will be the total size of the output array INCLUDING the start and end colors.

function generateColorGradient(colorStart, colorEnd, colorCount) {

    /* Convert a hex string to an RGB triplet */
    var convertToRGB = function(hex) {

        /* Remove '#' in color hex string */
        var trim = function(s) {
            return (s.charAt(0) == '#') ? s.substring(1, 7) : s;
        };

        var color = [];
        color[0] = parseInt((trim(hex)).substring(0, 2), 16);
        color[1] = parseInt((trim(hex)).substring(2, 4), 16);
        color[2] = parseInt((trim(hex)).substring(4, 6), 16);
        return color;

    };

    /* Convert an RGB triplet to a hex string */
    var convertToHex = function(rgb) {

        var hex = function(c) {
            var s = "0123456789abcdef";
            var i = parseInt(c);
            if (i == 0 || isNaN(c))
                return "00";
            i = Math.round(Math.min(Math.max(0, i), 255));
            return s.charAt((i - i % 16) / 16) + s.charAt(i % 16);
        };

        return hex(rgb[0]) + hex(rgb[1]) + hex(rgb[2]);

    };

    var start = convertToRGB(colorStart);
    var end = convertToRGB(colorEnd);

    // The number of colors to compute
    var len = colorCount - 1;

    //Alpha blending amount
    var alpha = 0.0;

    var ret = [];
    ret.push(convertToHex(start));

    for (i = 0; i < len; i++) {
        var c = [];
        alpha += (1.0 / len);
        c[0] = end[0] * alpha + (1 - alpha) * start[0];
        c[1] = end[1] * alpha + (1 - alpha) * start[1];
        c[2] = end[2] * alpha + (1 - alpha) * start[2];
        ret.push(convertToHex(c));
    }

    ret[len] = convertToHex(end); // last color might be slightly off

    return ret;

}

One thing that I noticed is that for colorCount values greater than 13 the final color was not quite the requested final color. Rounding error I suppose. That's why I explicitly set the last element in the return array to the end color, if you were wondering.

Using jQuery, I wrote a simple demonstration of the gradient function:

    var things = generateColorGradient("#CC0033", "#FFFF66", 15);
    for (var i = 0; i < things.length; i++) {
        $("body").append("<div style='background-color: #" + things[i] + ";'>test</div>");
    }


...which produces the following output on a web page. Beautiful, isn't it?


Today's comic, on the other hand, contains absolutely nothing of any technical interest. You should still read it though. And if you haven't read all the other Amphibian.com comics, what are you waiting for? They don't take that long to read. Start today!

Amphibian.com comic for 1 February 2016