Monday, November 30, 2015

Adding Emoji to a Web Page

I've decided that emoji are the 21st century version of cave wall drawings. What ever happened to using words?
I draw frog pictures online.
Emoji set designed and offered free by Emoji One
Anyway, I wanted to do a comic where the frogs talked to each other using emoji, but I quickly encountered several problems. The native emoji on Windows look terrible, and are incomplete. And knowing exactly what my mobile users would see was impossible due to inconsistencies between the different phone emoji sets. Additionally, I would have to make some code changes to Amphibian.com's editor because it destroys the extended Unicode character values that are required to write in emoji. As it turns out, emoji on the desktop web is a legitimate problem.

But there is a solution! I found Emoji One, open-source emoji engineered for the web. This is a great project that made adding emoji support to Amphibian.com extremely simple.

First, they provide their JavaScript and CSS files via CDN, so I didn't have to really install anything directly into my web application. Just a few lines of code into my header template...

<script
  src="//cdn.jsdelivr.net/emojione/1.5.2/lib/js/emojione.min.js">
</script>

<link rel="stylesheet"
  href="//cdn.jsdelivr.net/emojione/1.5.2/assets/css/emojione.min.css"
/>

Once that was done, I had a few options. They provide functions for converting both Unicode and shorthand (such as :frog:) into pictures. I decided to go with the shorthand version for my site, mainly because it allowed me to more easily write the comics from my desktop browser (Chrome on Windows).

When I write some lines for a frog's speech bubble, I can type

:pizza: + :bowling: @ :clock730: :question:

which is then converted to emoji when the page loads. It's not 100% automatic, though. I had to add a few lines of JavaScript to my site but it was really simple.

// Emojis! (See http://emojione.com/developers)
$(function() {

    $('.bubble, .free-text').each(function() {
        var origText = $(this).html();
        var eText = emojione.shortnameToImage(origText);
        $(this).html(eText);
    });

});

The above code relies on jQuery, which I use on my site. As soon as the page is done loading, it finds every element on the page that has a class of bubble or free-text (the classes I use for speech bubbles and floating text areas) and converts the contents to include emoji images. It happens fast enough that I haven't been able to catch the shorthand text in there, but there could theoretically be some flicker. If I was more serious about this I could do the conversion on the server side instead...but this is just for a comic about frogs.

Make sure you read today's emojinal comic so that you can appreciate all the work I had to do for it. I'm not sure if I'll use emojis in the future, but you never know...

Amphibian.com comic for 30 November 2015

Friday, November 27, 2015

More Features for your Jade Templates

If you've seen Amphibian.com today you might notice the Christmas lights hanging from the header. I wanted to decorate the site a little, but I was worried that I might forget to take them down at the end of December. I think I left the lights up on my house until March last year. It was really cold and I didn't want to go outside.


To prevent this from happening to my website, I decided to add some code which will check the date and automatically display the lights only during the Christmas season. Since the HTML for Amphibian.com is generated by the Jade template system and Express, conditionally including some lights could be accomplished by putting a date check in the template for the comic page. The problem? The check for the Christmas season is a little more complicated than I cared to cram into a Jade template. And Jade/Express doesn't provide any utility libraries by default that help in this situation.

Fortunately, it's rather simple to add your own modules that can be used in your templates.

First, I made a holiday module that I can use to determine if we are currently in the Christmas season.

module.exports = function() {

    return {

        /*
         * Returns a Date object that represents Thanksgiving (US)
         * for this year. The fourth Thursday in November.
         */
        thanksgiving: function() {

            var d = new Date();

            d.setDate(1);
            d.setMonth(10);

            while (d.getDay() != 4) {
                d.setDate(d.getDate()+1);
            }

            d.setDate(d.getDate()+21);

            return d;

        },

        /*
         * This function returns true during the Christmas season.
         * I define that as Thanksgiving through the end of December.
         */
        isChristmasSeason: function() {

            var monthToday = (new Date()).getMonth();
            var dateToday = (new Date()).getDate();

            return (monthToday == 11 || (monthToday == 10 && dateToday >= this.thanksgiving().getDate()));

        }

    };

};

This is a standard Node module that exports an object containing two functions. One returns a Date object for Thanksgiving in the current year. In the United States, Thanksgiving is the 4th Thursday in November, so the function finds the first Thursday and then adds 3 weeks. The second function returns true if the current date falls within the Christmas season. I have personally defined that as Thanksgiving (hence the need for the first function) through the end of December.

Now to enable this module in Jade templates, you just need to add it to the locals object in the Express app. Here's an example of how to do that with this particular module:

var express = require('express');

var app = express();

app.locals.holiday = require('./holiday')();

Any fields on the locals object are available in Jade templates. In my comic page template, I can now do do this:

doctype html
html
 include head
 body
  include header
  if holiday.isChristmasSeason()
   include holidayHeader
  div#comicArea
   include comic
  include footer
  include js

The holiday object can be used anywhere it's legal to use JavaScript expressions in the template. I use it in the if block to see if I need to include the special holiday header.

You can use this technique to add whatever module you want to locals and then use them inside your templates. While Jade is not perfect, this capability adds a lot of flexibility to the framework.

Please continue to enjoy this Christmas season by viewing today's comic, which is, appropriately, about the Christmas season.

Amphibian.com comic for 27 November 2015

Wednesday, November 25, 2015

Small But Important Game Update

Today I took a break from adding any more serious capabilities to my Phaser platformer so that I could focus on a small but important change - the Science Frog images!

I said from the beginning that this was supposed to be a Science Frog game, but so far Science Frog has not been seen. That's because I re-used the frog spritesheet from the soccer game just so I could get stuff working in the code. Drawing the images always takes me a while and I prefer to get the coding first. At some point, though, I have to stop and fix the images.

I did sneak in a few minor code tweaks along with the new images. First of all, I am using half as many images as I was before. I only have pictures of Science Frog facing right. Instead of switching to a reverse animation sequence when he turns around, I am flipping the sprite by setting the scale.x value to be -1 or 1 depending on which direction he is facing.

frog.anchor.setTo(0.5, 0);

// normal direction for the sprites
frog.scale.x = 1;

// reverse direction for the sprites
frog.scale.x = -1;

The important thing here is that for the scale flip to work, the anchor of the sprite has to be 0.5 on the x-axis. Otherwise, the image gets drawn someplace you don't expect!

The other change is to use stop the animation and use only a certain frame when Science Frog is jumping or falling. This stops the effect of him flailing his arms and legs if you hold the arrow keys down while jumping. That just looked weird. For that, I just check to see if the y-velocity is non-zero and then set the sprite's frame accordingly.

function setAnimation(f) {

    if (f.body.velocity.y === 0) {

        // frog is on the ground

        if (f.body.velocity.x === 0) {

            // no animation, the frog is still
            f.animations.stop(null, true);

        } else {

            if (f.body.velocity.x > 0) {
                f.scale.x = 1;
            } else if (f.body.velocity.x < 0) {
                f.scale.x = -1;
            }
            f.animations.play("run");

        }

    } else {  // frog is jumping up or falling down

        // no animation, use a fixed frame
        f.animations.stop(null, true);
        
        if (f.body.velocity.y < 0) {
            f.frame = 1;
        } else if (f.body.velocity.y > 0) {
            f.frame = 2;
        }

    }

}

That's all for today! Remember, you can play the game here, and view the complete source code on GitHub. Have a Happy Thanksgiving, and be sure to read today's comic before you prepare your turkey.

Amphibian.com comic for 25 November 2015

Monday, November 23, 2015

Attacking Enemies in my Phaser Platformer

Tossing Pencils
After adding some evil toads to my 8-bit style platformer last week, there were finally some things to avoid while jumping around in the game. Unfortunately, there was no way to defeat the toads. Typically, most enemies in these kinds of games can be killed in some way. This weekend I decided to add an attack capability to the game.

After debating an attack style for a little while, I decided that the basic attack capability should be for the frog to throw pencils. This is supposed to be Science Frog's game, but I haven't updated the frog sprite yet. As a basic attack, I think Science Frog would toss pencils.

Adding some attack pencils to the game turned out to be fairly simple. Phaser really does make game development easy!

The first step was to add a group of weapons to the game, along with a new key tracker to use for firing them off. I also added a variable called nextFire to limit how fast weapons can be thrown.

var weapons;

var FIRE_RATE = 250;
var nextFire;

function create() {

    // ... other stuff ...

    weapons = game.add.group();
    weapons.createMultiple(3, 'pencil', 0, false);

    // ... other stuff ...

    fire = game.input.keyboard.addKey(Phaser.Keyboard.CONTROL);
        
    nextFire = game.time.now + FIRE_RATE;

}

The createMultiple function on Phaser.Group objects takes 4 parameters. The first is the number of sprites to create. The second is the name of the sprite image to use. I added a "pencil" image to my assets for this purpose. The third parameter is the sprite frame to use, but my pencil only has one frame. The fourth parameter indicates if the sprite should exist. I set this one to false. Why would I create sprites that don't exist? How do I use them if they don't exist? Well, sprite "existence" really indicates if the sprite is currently interacting with the rest of the game. You can set a sprite's existence to false in order to temporarily disable it - and then re-use it later. That's exactly what I'll be doing.

The nextFire variable is set to a time in the future. That gets used later on, keep reading.

function update() {

    game.physics.arcade.collide(frog, layer);
    game.physics.arcade.collide(enemies, layer);
    game.physics.arcade.overlap(frog, enemies, hurtFrog, null, this);

    // see if weapons are hitting enemies
    game.physics.arcade.overlap(enemies, weapons, hurtEnemy, null, this);
    
    // ... other stuff ...

    if (fire.isDown) {
     throwSomething();
    }
    
    // ... other stuff ...

}

I added another overlap check, this time between the enemies group and the weapons group. If a weapon is overlapping an enemy, the hurtEnemy function will be called.

The other new piece of the update function is the check to see if the fire key is being held down. I used the CTRL key for this. If it is down, the throwSomething function is called.

First, the hurtEnemy function:

function hurtEnemy(e, w) {
    
    if (!e.immune) {
        e.immune = true;
        e.alpha = 0.5;
        e.damage(0.1);
        
        w.exists = false;
        
        game.time.events.add(200, function() {
            e.immune = false;
            e.alpha = 1;
        }, this);
    }
    
}

This function is very similar to the hurtFrog function I added last week. Here, the first parameter will be the enemy and the second will be the weapon that hit it. Just like I did for the frog, I use an immune flag to make sure the enemies can't get hurt too fast, and if they are hit there is a change to their alpha channel. After 200 milliseconds, the immune flag goes off and the enemy can get hurt again. The big difference between this and the hurtFrog function is the line that sets exists to false for the weapon. This will immediately make the weapon disappear so it can be recycled by the throwSomething function. And speaking of that function. here it is:

function throwSomething() {

    if (game.time.now > nextFire) {

        nextFire = game.time.now + FIRE_RATE;
        
        var weapon = weapons.getFirstExists(false);
        if (weapon){
            weapon.exists = true;
            weapon.anchor.setTo(0.5, 0.5);
            weapon.lifespan = 1500;
            weapon.reset(frog.body.position.x+20, frog.body.position.y-20);
            game.physics.arcade.enable(weapon);
            weapon.body.velocity.y = -400;
            weapon.body.angularVelocity = 50;
            if(frog.direction == 1){
                weapon.body.velocity.x = 500;
            }else{
                weapon.body.velocity.x = -500;
            }
            
        }

    }

}

It seems like there's a lot going on here, but it's really very simple. First, the whole thing is wrapped in a check to see if the current game time is greater than the next fire time. If not, nothing happens. This ensures that the fire rate is constrained. If firing is allowed, the nextFire time is reset to some time in the future and then the fun happens.

The call to weapons.getFirstExists(false) gets the first weapon from the group that doesn't currently exist. Remember, the weapons will get set to not exist after they hit an enemy. If we have a weapon in that state, it gets set to exist again. It also gets a lifespan set on it, so that it will automatically set itself to not exist even if it doesn't hit an enemy after a certain amount of time. Its position gets reset to around the middle of the frog (since he's supposed to be throwing it), and it gets some velocity given to it. That's what makes it fly through the air. I set both x and y velocity so that it moves in a kind of arc motion. I may give different types of weapons different motion attributes at some point.

With those simple additions to the game, you can now throw pencils at frogs. Give it a try for yourself at http://amphibian.com/eight-bit or browse the complete source code on GitHub.

Also remember to check out today's comic! Science Frog doesn't throw any pencils in it.

Amphibian.com comic for 23 November 2015

Friday, November 20, 2015

Enemy Collisions in my Phaser Platformer

Earlier in the week I added some enemies to my 8-bit style Phaser platformer, but they were basically just images in the level. Bumping in to them didn't really do any damage. There was a little more work I had to do in order for them to become proper enemies.

If you read my post from Wednesday, you'd know that I created all the enemies from data in an object layer in the map. When I did that, I put all the enemy objects in a group called enemies. All of that took place in the game's create function. Checking collisions and hurting the frog takes place in the update function.

Here's what I wanted to happen. If the frog touches an enemy, he should get hurt. How much should probably vary by enemy but that's not extremely important at the moment. When the frog gets hurt, there should be some kind of visual indicator. For now, I've decided to set the frog's alpha channel to 50% for 500 milliseconds to indicate damage. For those 500 milliseconds, the frog can't be hurt again, and he should be thrown away from the enemy that inflicted the damage.

First, I added some fields to the frog object when I create it.

function createFrog() {

    // ... other stuff ...

    f.immune = false;
    f.health = 3;

    // ... other stuff ...    
    
}

The immune field is just something I made up that should indicate if the frog is currently in that 500 millisecond period where he can't get hurt again. He's immune from further damage. The health field is actually provided by Phaser. You can assign a health value to a sprite, and then change it by calling damage or heal. When health reaches 0, the sprite is killed. The maximum value is arbitrary, so I set it to 3 for now.

Now in the update function, I add some more collision checks. First, I added a collision check between the enemies and the layer object. This will make the enemies sit on the ground and logs and stuff just like the frog does. Then I added an overlap check between the frog and the enemies. Why overlap instead of collision? If I checked for collision, the frog would push against the enemies and possibly even move them. I wanted encounters with enemies to only impact the frog. Checking for overlap allows me to call a function when two sprites are in collision but Phaser takes no action to remove the sprites from the collision state - I'll handle that manually. That's why I give the third parameter, hurtFrog, to the overlap function. The hurtFrog function will be called when overlap is detected. Here are the important parts of the update function:

function update() {

    game.physics.arcade.collide(frog, layer);
    game.physics.arcade.collide(enemies, layer);
    game.physics.arcade.overlap(frog, enemies, hurtFrog, null, this);

    if (!frog.immune) {
        frog.body.velocity.x = 0;
    }

    // ... other stuff ...

    if (!frog.immune) {
        if (cursors.left.isDown) {
            frog.body.velocity.x = -150;
        } else if (cursors.right.isDown) {
            frog.body.velocity.x = 150;
        }
    }

    // ... other stuff ...

}

The other changes to update are to only set the frog's velocity if the frog is not in that immune state. This is basically to override the player's control over the frog. I'm going to essentially toss the frog out of the way regardless of what key the player might be holding down.

...Which brings us to the the hurtFrog function. This is where the action is!

function hurtFrog(f, e) {
    
    if (!f.immune) {
        f.immune = true;
        f.alpha = 0.5;
        f.damage(0.1);
        if (f.body.position.x < e.body.position.x) {
            f.body.velocity.x = -300;
        } else {
            f.body.velocity.x = 300;
        }
        game.time.events.add(500, function() {
            f.immune = false;
            f.alpha = 1;
        }, this);
    }
    
}

Remember, this function will be called by Phaser when the frog overlaps with an enemy. The first parameter passed in will be the frog object and the second will be the enemy. This is the same order that they object are specified in when calling overlap. In this function, I first check to see if the frog is currently in that immune state. If he is, I do nothing. But if he isn't, I set the immune flag, set the alpha channel to 0.5, and do a little damage. Then comes the frog-tossing! To throw the frog out of the way of whatever enemy he just hit, I check to see where he is in relation to the enemy. If he's on the left side, I give him some velocity to toss him to the left. If he's on the right, I toss him to the right. Finally, I set that 500 millisecond timer. When time's up, the immune flag goes off and the alpha channel goes back to 100%. At this point, the frog can get hurt again.

You can give the game a try for yourself and let me know what you think. I am still playing around with the numbers for the toss velocity and such. The whole point of this sandbox level is to get the right feel for the game before I start making the real levels. The complete source code is on GitHub as well, if you want a closer look.

Also, don't forget to have a closer look at today's comic!

Amphibian.com comic for 20 November 2015

Wednesday, November 18, 2015

Adding Enemies to my Phaser Platformer

So far, my 8-bit style platformer has been nothing but a frog walking around. It was too safe. There was nothing bad for the frog to hit. To make a proper platformer, there must be some enemies. In response to this problem, I added some toads.

To accomplish this, I first added an Object Layer to my game map in Tiled. On this layer, I placed some tiles that were pictures of the toad. You don't actually have to use tiles here - the objects are only placeholders for Sprites that will be added in the game. You could just use rectangles or whatever, but I think it's nice to be able to see the enemies in the map editor.

In my example, as seen below, I called my Object Layer "others." The name will be important later.

Before changing the game code to utilize this new layer, I also had to add the toad spritesheet to my assets pack. The key name is "toad." Here is that part of the pack.json file:

    {
        "type": "spritesheet",
        "key": "toad",
        "url": "assets/sprites/toad.png",
        "frameWidth": 64,
        "frameHeight": 64
    }

Now it's time to make the toad appear in the game. This is made (almost) easy by Phaser's map.createFromObjects function. It takes objects from an Object Layer in your map and turns them into Sprites. I added the following lines to my game's create function:

enemies = game.add.group();
enemies.enableBody = true;

map.createFromObjects('others', 6571, 'toad', 0, true, false, enemies);

You should be asking, "What do those parameters mean? Especially that 6571." The first parameter is simply the name of the object layer. Remember, I named mine "others". The second parameter is tricky - it is the gid of the objects that should be converted to Sprites. Where is the gid set, or even displayed, in Tiled? I have no idea. The only way I could find it was to look in the JSON after I saved the map. Basically, if you use a tile as your object all of them that use the same tile will have the same gid. You just have to figure out that that gid is. The following snippet from the map JSON shows the gid:

     "objects":[
            {
             "gid":6571,
             "height":64,
             "id":14,
             "name":"toad1",
             "properties":
                {
                },
             "rotation":0,
             "type":"toad",
             "visible":true,
             "width":64,
             "x":1056,
             "y":704
            }] 

The third parameter is the name of the spritesheet from the asset pack, and the fourth parameter is the frame to use as a start. My toad sprite only has one frame right now, so I just set that one to 0. The next parameter is if the new Sprite should exist. I don't know why this would ever be set to false, so I set it to true. The sixth parameter selects for auto-culling. Auto-culled sprites are culled from the camera if not in range. The final parameter that I use is the group in which all these new sprites should be placed. I put them all in an enemies group so that it's easy to check for collisions later.

With these simple steps, I was able to add some evil toads to my game - and adding more is just as simple as editing the map. Phaser makes it very convenient for you to do stuff like this. If you want to see the game in action, it is playable at http://amphibian.com/eight-bit/. And make sure you read today's comic as well!

Amphibian.com comic for 18 November 2015

Monday, November 16, 2015

Review of my TopWebcomics Ad

Instead of doing another post about game progress today, I thought that I'd just give a quick update on my ad I ran on TopWebcomics.com for the past several weeks. My run just ended on Saturday, and I'd like to review it.

To review, I put a special parameter in the link from the ad that I could use to track people who visit Amphibian.com as a direct result of clicking the ad. Here's the stats for the past 4 weeks:

Total Comics Viewed44,121
Comics Viewed by Someone Who Clicked on the Ad15,285
Comics Viewed not related to the Ad28,836

So much like last time I checked, which was only a few days after the ad started running, about a third of the comics viewed were a direct result of the advertising. Combine this with the fact that overall site traffic has increased by around 50% since the ad started, and I would have to conclude that TopWebcomics advertising has blown away my Facebook advertising in terms of getting traffic to the site. As a bonus, 6 people added my comic to their "Favorites" list on TopWebcomics.com over the past 4 weeks. While that doesn't seem like a big number, it is an increase of 50% as well.

My ad, in case you missed it.
I ended up with a cost-per-click of around $0.04. But once someone clicked through to my site, it was highly likely that they stayed and viewed several comics. Most of the increase in views was a direct result of people browsing through the complete set of comics after visiting the site for the first time. About 25% of the people who originally came from the ad came back on their own at least once.

Overall, I would say that I am highly satisfied with my experience advertising with TopWebcomics. I would do it again.

In other news, the test level for the 8-bit style platformer that I'm making with Phaser is now live at https://amphibian.com/eight-bit. Give it a try if you have a few minutes, and feel free to send feedback. I'll try to make some significant updates this week, but I'm also working on adding some new features to the comic as well. Here's the link to today's comic, even without those new features.

Amphibian.com comic for 16 November 2015

Friday, November 13, 2015

Hit the Ground? Play a Sound!

I didn't get to add any enemies to my Phaser 8-bit style platformer this week, but I did add some sound effects. Even though I hadn't used background music before in a game before, it was very easy to add. Playing a sound when the frog jumps was also simple.

But what about playing a sound when the frog lands back on the ground? Not as easy.

First, the simple sounds. For background music, I used "Ouroboros" by Kevin MacLeod (http://incompetech.com). The jump and landing sounds were public domain from FreeSound.org. I added them all to my asset pack file. I gave the background music the key bgmusic1, the jump sound the key jump, and the landing sound the key thud.

In the create function I made the sound objects.

function create() {

    // ... other create stuff ...

    bgmusic = game.sound.add("bgmusic1");
    bgmusic.volume = 0.3;
    bgmusic.loop = true;
    bgmusic.play();

    jumpSound = game.sound.add("jump");
    thudSound = game.sound.add("thud");

}

I start playing the background music right away, after setting it to loop and lowering the volume. I don't like the background music so loud that you can't hear the other sound effects. In Phaser, volume is set with a number between 0 (not audible) and 1 (full volume).

What do I do with the jump and thud sounds? Jump is easy.

function update() {

    // ... other stuff ...

    if (spacebar.isDown) {
        if (frog.body.onFloor() && jumpTimer === 0) {
            // jump is allowed to start
            jumpTimer = 1;
            frog.body.velocity.y = -400;
            jumpSound.play();
        } else if (jumpTimer > 0 && jumpTimer < 31) {
            // keep jumping higher
            jumpTimer++;
            frog.body.velocity.y = -400 + (jumpTimer * 7);
        }
    } else {
        // jump button not being pressed, reset jump timer
        jumpTimer = 0;
    }

    // ... other stuff

}

In the jump function I made on Wednesday, I simply add a call to jumpSound.play() there on line 10 when a jump starts. Couldn't be easier. Now for the hard part...

Playing a sound when the frog collides with the ground seemed obvious at first - just call thudSound.play() in the optional collision function. The collide function lets you specify an optional function that is called when the sprites being checked actually hit each other. But this is not a good place to play a thud sound. The reason is that the frog and the ground are in collision for every frame in which the frog is sitting on the ground. The sound will play all the time, except when the frog is in the air because of a jump or a fall. Not exactly what I wanted.

function update() {

    game.physics.arcade.collide(frog, layer, function(f, t) {
        // -------------------------------------------
        // don't do this!
        // the sound plays almost all the time!
        // -------------------------------------------
        thudSound.play();
    });

    // ... other stuff ...    

}

The trick is to set a flag when the frog is falling - that is, when the y-velocity is greater than 0. This could happen because he jumped or because he walked off a ledge. In either case, gravity pulls him down and the game engine sets the y-velocity to a value greater than 0. When this happens, I set a falling flag on the frog object. Then, I check to see if the frog's physics body is on the floor and the falling flag is true. If these two cases are both met, it means that the frog just hit the ground after a fall. It's there that I call thudSound.play() and reset the falling flag to false. That was some alliteration.

function update() {

    game.physics.arcade.collide(frog, layer);

    if (frog.body.onFloor() && frog.falling) {
        frog.falling = false;
        thudSound.play();
    }

    // ... other stuff ...    

    if (frog.body.velocity.y > 0) {
        frog.falling = true;
    }
    
}

Now whenever the frog hits the ground, a thud sound is heard just once. I'm happy with these results and you'll be happy with today's comic. And remember, you can get the full source code of this game on GitHub! Okay, now for the comic:

Amphibian.com comic for 13 November 2015

Wednesday, November 11, 2015

Tuning the Jumps in my Phaser Platformer

I found just a few minutes to work on my 8-bit-style Phaser platformer since the weekend. Just about everyone in my family is sick in some way, including myself. So with my limited time I decided to fine-tune the jumps.

In my really basic code so far, a jump was accomplished by hitting space on the keyboard. If the frog was on the ground while space was pressed, the frog's y-velocity was set to a constant number. This worked fine, but it resulted in every jump being the same height. Tapping the spacebar for a millisecond caused the same jump as holding down the key for ten seconds. Well, actually holding down the key caused the frog to jump over and over and over again. I wanted to fix that too.

I was looking for a way to make a longer keypress result in more height, up to a limit. After reading a few ideas tossed out there by other people on the Internet, I decided on what to do. Here is part of my new update function, with the enhanced jump logic (remember, you can find the complete source code on GitHub):

var jumpTimer = 0;

function update() {

    game.physics.arcade.collide(frog, layer);

    if (spacebar.isDown) {
        if (frog.body.onFloor() && jumpTimer === 0) {
            // jump is allowed to start
            jumpTimer = 1;
            frog.body.velocity.y = -400;
        } else if (jumpTimer > 0 && jumpTimer < 31) {
            // keep jumping higher
            jumpTimer++;
            frog.body.velocity.y = -400 + (jumpTimer * 7);
        }
    } else {
        // jump button not being pressed, reset jump timer
        jumpTimer = 0;
    }

    // ... other stuff ...

}

Here's how it works... I've introduced a new variable, jumpTimer, which will keep track of how long the jump lasts. If the spacebar is not being pressed, jumpTimer is always set to 0. No jumping is taking place. But when the spacebar is being pressed, some other stuff happens.

First, I check to see if the frog is on the floor of something and jumpTimer is 0. Obviously, I only want a jump to start if the frog is on the ground. But the other part of that ensures that a new jump can start only if jumpTimer has been reset - and it won't be reset until the player releases the space key. This prevents the endless bouncing when holding down the button. Anyway, if both of those conditions are met, jumpTimer is set to 1 and the frog's y-velocity is set to -400. Why -400? I just liked that value after playing around with it. You might want a different value.

In the other case, jumpTimer is greater than 0 but less than 31, the jump is continuing. Each time in here, jumpTimer is incremented. This happens once per frame, so this function keeps the frog moving up for a maximum of 30 frames. After that, no more velocity will be added and gravity pulls the frog back to the ground. I reduce the amount of velocity added by the value of jumpTimer times 7 in order to slow down the vertical rise the longer the button is held down. This looked slightly more natural. Again, I chose 7 just by playing around with it until I found a number I liked.

Animation of the new jump tests.

I'm happy with how it turned out. Next I think I'll try to add an enemy or something and work on the attack mechanics. While you wait for me to write about that, read today's comic. It's almost time to start thinking about Christmas shopping. Maybe it's already that time. Depends on how much of a procrastinator you are.

Amphibian.com comic for 11 November 2015

Monday, November 9, 2015

Single-Direction Collision for your Phaser Tiles

I was working on my platformer (full source code on GitHub) using Phaser, and I wanted to add some platforms to the map. You know, like you do in platformers. Specifically, I wanted to have some box-like things in the environment that the frog could jump on top of, but also walk in front of. Like those box things with the screws in the corners from Super Mario Bros. 3. Mario walked in front of them, but if he jumped up he would land on top and walk across them. You remember:

I wanted boxes like the one upon which Mario stands.

The tileset I was using had some things like that in it, so I used them and made some nice platforms in my sandbox level. It was then I realized that I had a problem. If I set the tiles that made up the boxes to collide with the frog, he could jump on them but not walk in front of them. Maybe if I just set the top tiles as having collisions, or put the lower halves in the background layer, I could walk in front of them. Yes, but I still couldn't jump straight up and land on top because the frog would bump into the top tiles from below.

I want to jump on these.

Phaser supports one-way collisions for physics bodies with the Arcade Physics module that I am using, but since I was dealing with a tilemap there didn't seem to be a way to set it up. When you want to set up collision with some of your tiles, you use one of the setCollision functions (setCollision, setCollisionBetween, setCollisionByExclusion). They don't take any parameters to fine-tune the collisions. Internally, those functions call setCollisionByIndex. Looking at the source for that function, I could see that every tile was indeed set to collide on all sides. It took me a little while, but I found a way to override that behavior and get the effect I wanted.

Here is the function I came up with:

function setTileCollision(mapLayer, idxOrArray, dirs) {

    var mFunc; // tile index matching function
    if (idxOrArray.length) {
        // if idxOrArray is an array, use a function with a loop
        mFunc = function(inp) {
            for (var i = 0; i < idxOrArray.length; i++) {
                if (idxOrArray[i] === inp) {
                    return true;
                }
            }
            return false;
        };
    } else {
        // if idxOrArray is a single number, use a simple function
        mFunc = function(inp) {
            return inp === idxOrArray;
        };
    }

    // get the 2-dimensional tiles array for this layer
    var d = mapLayer.map.layers[mapLayer.index].data;
    
    for (var i = 0; i < d.length; i++) {
        for (var j = 0; j < d[i].length; j++) {
            var t = d[i][j];
            if (mFunc(t.index)) {
                
                t.collideUp = dirs.top;
                t.collideDown = dirs.bottom;
                t.collideLeft = dirs.left;
                t.collideRight = dirs.right;
                
                t.faceTop = dirs.top;
                t.faceBottom = dirs.bottom;
                t.faceLeft = dirs.left;
                t.faceRight = dirs.right;
                
            }
        }
    }

}

Despite the weird-looking top half of the function that is just setting up a way of matching against either a single tile id value or an array of values, overall the function is very simple. It gets the 2-dimensional array of all the tiles in the given layer and loops over them. If the tile index matches one of the specified index values, the collision settings for that tile are altered in accordance with the dirs object. The dirs object should have 4 boolean fields, top, bottom, left, and right. A value of true for any of them indicates that you want Phaser to check for collisions with other objects coming from that direction. In my case, only top was set to true. I only want to cause a collision when the frog hits the tile from above.

Here is how I use it from my create function:

function create() {

    game.physics.startSystem(Phaser.Physics.ARCADE);
    game.physics.arcade.gravity.y = 1500;

    var map = game.add.tilemap("stage1");
    map.addTilesetImage("ground", "tiles");

    layer = map.createLayer("layer1");
    layer.resizeWorld();

    map.setLayer(layer);
    
    // set collisions on ground tiles
    map.setCollisionBetween(1, 275);

    // set collisions on tiles that are the top parts of the boxes
    map.setCollisionBetween(3354, 3356);
    map.setCollisionBetween(2900, 2903);
    map.setCollisionBetween(3714, 3716);

    // set those box top tiles to only collide from above
    setTileCollision(layer, [3354, 3355, 3356, 2900, 2901, 2902, 2903, 3714, 3715, 3716], {
        top: true,
        bottom: false,
        left: false,
        right: false
    });

    // ... other stuff ...    

}

The end result is just what I wanted - the frog can walk in front of the boxes and jump up to land on top of them. The following video shows the box-jumping in action.


And speaking of jumping on boxes - if it's that time of year at your workplace in which you check boxes on reviews of your coworkers, you might enjoy today's comic. In it, we find that the frogs have a more basic approach to the classic forced ranking system.

Amphibian.com comic for 9 November 2015

Friday, November 6, 2015

Background Layers in a Phaser Platformer

I'm still playing around with my 8-bit style platformer that I'm building with Phaser. In my sandbox level I'm trying to get the feel of the game. One of the things I wanted was to have trees (or some other scenery) in the background that would scroll at a reduced rate. This creates the illusion of depth. Fortunately, Phaser provides me with a simple solution.

First, I used Tiled to add a background layer to my map. In the "Layers" pane, there's an icon for adding a new layer on the lower left. I called my background layer simply bg. The order is important - the lower the layer is in the last, the further back it is in the display. After creating the new layer I had to drop it down with the down-arrow icon (right next to the new layer icon).

Adding a background layer in Tiled

With the bg layer selected, I added some trees to my map and saved it. At that point I could modify the game code to make use of the new layer.

First of all, adding a background layer and then doing nothing resulted in failure. The frog was no longer colliding with the map tiles. I believe this was caused by the fact that map.setCollisionBetween works on the current layer, and when I had a second layer the current one was not what I expected. I had to call map.setLayer explicitly before turning on collisions.

To get the slower-scrolling effect on the background layer, I simply set the scrollFactorX property of the background layer to 0.5. With that, the horizontal scroll with be half that of the main layer. You can play around with that number to get faster or slower scroll. A value of 1.0 is the default scroll speed. Setting the value over 1 will make the layer scroll faster, and values under 1 make it scroll slower.

Here is my updated create function.

function create() {

    game.physics.startSystem(Phaser.Physics.ARCADE);

    game.physics.arcade.gravity.y = 1500;
     
    game.stage.backgroundColor = "#D3EEFF";
  
    var map = game.add.tilemap("stage1");
    map.addTilesetImage("ground", "tiles");

    // create the background layer
    var bglayer = map.createLayer("bg");
    bglayer.scrollFactorX = 0.5;

    layer = map.createLayer("layer1");
    layer.resizeWorld();

    // setup tile collisions with main layer
    map.setLayer(layer);
    map.setCollisionBetween(0, 6569);

    group = game.add.group();

    frog = createFrog(group, 50, 50, "frog", 200, "left");

    game.camera.follow(frog);

    cursors = game.input.keyboard.createCursorKeys();
    spacebar = game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);

}

This short video clip shows the scrolling effect in action.


Next I'm going to work on fine-tuning some of the tile collisions and then maybe fix the frog spritesheet (I'm still using the one from Frog Soccer - not quite right for this game). At some point I need to add an enemy as well and try to work out some sort of attack capability. Check back Monday! In the mean time, enjoy today's comic.

Amphibian.com comic for 6 November 2015

Wednesday, November 4, 2015

Building a Basic Platformer with Phaser

Like I said last week, I've started work on a new game for Amphibian.com. I'm once again using the Phaser HTML5 game framework, but this time I'm trying to make a classic platformer. Specifically, I want to make a game staring Science Frog in the style of NES Megaman.

While very similar to the approach I took creating Frog Soccer, this game will use very different mechanics. It needs to be side-scrolling, include jumping (and also falling), and have some sort of enemies that must be defeated.

To make this game, my first goal was to create a sandbox stage to test things out. I've been told that this is an important part of making any game. It lets you play around with the mechanics until you get the right behavior. After that, you can make the "real" levels.

My simple map in Tiled 

Dealing with the side-scrolling part was simple. In much the same way I created the soccer field for my last game, I used Tiled to make a map that was wider than it was tall. To get that 8-bit feel, I used a set of tiles from OpenGameArt.org. To start, I have a single layer named "layer1" and I called the tileset simply "ground." I saved the map in JSON format as "stage1.json" and added the tileset image, my frog spritesheet image, and the tilemap file to my pack.json file, shown here:

{
    "main": [
        {
            "type": "image",
            "key": "tiles",
            "url": "assets/images/nestle2.png"
        },
        {
            "type": "spritesheet",
            "key": "frog",
            "url": "assets/sprites/frog.png",
            "frameWidth": 79,
            "frameHeight": 60
        },
        {
            "type": "tilemap",
            "key": "stage1",
            "url": "assets/stage1.json",
            "data": null,
            "format": "TILED_JSON"
        }
    ]
}

Even though I only have 3 game assets at this point, putting them in an asset pack file now will save time later. In my game's preload function, I only need one line to load everything.

function preload() {

    game.load.pack("main", "assets/pack.json");

}

With all the assets loaded in preload, I can set up the game in the create function. Here's where things start to diverge from my last game. Here, I need to use gravity. The frog (and presumably other characters) should be pulled down to the bottom of the screen until they hit a platform or the ground. I want only gravity on the y-axis, so I set game.physics.arcade.gravity.y = 1500. Why 1500? I basically played around with it until I got the frog to fall as fast as I liked. I might change it later. The other thing here is that the frog has to stop falling when he lands on something - specifically one of the ground tiles. Phaser gives us an easy way to specify which tiles should have collision turned on. The map.setCollisionBetween function turns on collisions for all tiles with ids between two values. There is also a similar function that takes an array of ids instead of a range. In my example code below, I am basically turning on collision with all tiles. I might fine-tune this later.

function create() {

    game.physics.startSystem(Phaser.Physics.ARCADE);

    // set up gravity to pull everything down
    game.physics.arcade.gravity.y = 1500;

    game.stage.backgroundColor = "#D3EEFF";

    var map = game.add.tilemap("stage1");

    // associates "ground" from the tilemap with the "tiles" image
    map.addTilesetImage("ground", "tiles");

    // collide with everything for now
    map.setCollisionBetween(0, 6569);

    layer = map.createLayer("layer1");
    layer.resizeWorld();

    group = game.add.group();
    frog = createFrog(group, 50, 50, "frog", 200, "left");
    game.camera.follow(frog);

    cursors = game.input.keyboard.createCursorKeys();
    spacebar = game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);

}

Just turning on collisions for tiles isn't quite enough. In the update function, I have to check for collisions between the frog and the map layer. Again, this is very simple. There's also a trick to make sure holding the jump button (the spacebar in my case) doesn't cause the frog to fly up forever. Phaser's arcade physics gives us a function, onFloor, which returns true if and only if the body is at rest on a platform. If that is true, I give the frog upward velocity. The gravity setting I discussed above takes care of bringing him back down.

function update() {

    game.physics.arcade.collide(frog, layer);

    frog.body.velocity.x = 0;

    if (spacebar.isDown) {
        if (frog.body.onFloor()) {
            frog.body.velocity.y = -600;
        }
    }

    if (cursors.left.isDown) {
        frog.body.velocity.x = -150;
    } else if (cursors.right.isDown) {
        frog.body.velocity.x = 150;
    }

}

And with those steps, I have the basic functions of an 8-bit style platformer. I have a lot more work to do, and I'll keep writing about my progress here. As always, the complete source code is available on GitHub. Check it out!

My first HTML5 platformer made with Phaser!

Also, check out today's comic for a deep conversation about the use of COTS in software development...or pictures of frogs falling asleep on the job.

Amphibian.com comic for 4 November 2015

Monday, November 2, 2015

Nothing This Weekend!

Despite my positive outlook from Friday when I talked about starting my new game, I actually got nothing accomplished on it this past weekend. Two things happened.

First, Saturday was Halloween and my daughter's soccer team had an away-game. The game was delayed for a while because no referee showed up. By the time we got home, it was almost time for the trick-or-treat to start. Everyone quickly ate dinner and got into costume. The rest of the night was spent walking around the neighborhood asking for candy.

And then that stupid clock-change thing happened. Actually the inspiration for today's comic, I hate Daylight Savings Time. Well, Daylight Savings Time itself is fine - I hate changing the clocks. It messes me up. It doesn't matter that in the fall we supposedly gain an hour of sleep. I never really do. Especially when there are young children in my house that don't care what the clock says. They are going to get up at the same time no matter what. And now I'm getting hungry an hour before lunch, getting tired at 9:00 in the evening, and just off all day. Why do we do this to ourselves? I'm a big proponent of eliminating the whole time change thing. But I'm also in favor of eliminating local time zones and switching to metric time. Nobody cares what I think.

So instead of some Phaser game code and examples, today I'm just sharing a picture of my kids in their Halloween costumes. We get back to software on Wednesday.


Tasha, on the left, is wearing her R2-D2 dress, which I think counts as a costume. She stayed at our house and handed out candy while my wife and I walk around with the other two. Gladrielle, in the center, is Anna from Frozen. Alexandria, on the right, is Sabine from Star Wars Rebels. We had a total Star Wars theme going if you count the fact that "Anna" held a light saber while we walked around. Gladrielle thinks that Anna might have Force powers. They're all owned by Disney now, so it's plausible.

Here's the comic for today, although this 1-frame sample doesn't do it justice. You have to visit the site.

Amphibian.com comic for 2 November 2015