Monday, December 14, 2015

Putting a Health Meter in my Phaser Game

As some of you may have noticed, I haven't been making much progress on my 8-bit style platformer lately. A business trip to Texas, Christmas decorating, kids' school Christmas programs, and some minor surgery took away all of my spare time. This kind of thing happens sometimes (well, except for the surgery part - that was the first surgery I've ever had) and I don't mind at all. Getting ready for Christmas and hearing my children sing at school will always be more important than making games with Phaser.

But that surgery thing...I can certainly do without that. I'd much rather makes games with Phaser.

However, since all that stuff is done and I am sitting here recovering from the surgery, I might as well work on my game some more! Today I added the frog's health meter to the screen.

The health meter (or life bar, health bar, or whatever you like to call it) has been missing from the game so far. The frog could take a certain amount of damage but you could never tell just how much more he could take. I wanted to add a simple bar-of-color health meter to the screen so players could get some idea of how close they were to a Game-Over.

I ended up doing this by drawing colored rectangles in the game. It wasn't too difficult, but was a little different than putting image-based sprites on the screen. I had to create some Bitmap Data.

The Health Meter, showing that this frog has taken a little damage.

What exactly is Bitmap Data in Phaser? The Phaser.BitmapData object gives you the ability to draw graphics much like you would in a traditional HTML5 Canvas. You can think of it as lower-level access to the graphics, but you can also think of it as an alternative source of a Sprite's image. Instead of PNG image data, you could manually create an image using BitmapData.

Here's what I mean:
function createHealthBar() {

    meters = game.add.group();

    // create a plain black rectangle to use as the background of a health meter
    var meterBackgroundBitmap = game.add.bitmapData(20, 100);
    meterBackgroundBitmap.ctx.beginPath();
    meterBackgroundBitmap.ctx.rect(0, 0, meterBackgroundBitmap.width, meterBackgroundBitmap.height);
    meterBackgroundBitmap.ctx.fillStyle = '#000000';
    meterBackgroundBitmap.ctx.fill();

    // create a Sprite using the background bitmap data
    var healthMeterBG = game.add.sprite(10, 10, meterBackgroundBitmap);
    healthMeterBG.fixedToCamera = true;
    meters.add(healthMeterBG);

    // create a red rectangle to use as the health meter itself
    var healthBitmap = game.add.bitmapData(12, 92);
    healthBitmap.ctx.beginPath();
    healthBitmap.ctx.rect(0, 0, healthBitmap.width, healthBitmap.height);
    healthBitmap.ctx.fillStyle = '#FF0000';
    healthBitmap.ctx.fill();

    // create the health Sprite using the red rectangle bitmap data
    health = game.add.sprite(14, 14, healthBitmap);
    meters.add(health);
    health.fixedToCamera = true;

}

In this function, I create the health meter. It is essentially a black rectangle with a slightly smaller red rectangle inside of it. The size of the red rectangle will shrink as the frog's health decreases. First, I create a Phaser Group for these meters, then I create the bitmap data that will be used for the back of the meter. The call to game.add.bitmapData(20, 100) creates a BitmapData object 20 pixels wide and 100 pixels tall. The next 4 lines draw a filled rectangle which takes up the entire size of the bitmap. These lines are pretty much the same as you would use if you were working directly with the HTML5 Canvas context. After creating that bitmap, I create a Sprite using it as the key. If you were creating a sprite with an image file, this parameter would be the name of the image's key in the set of game assets - but here I can use the bitmap data directly instead. I now have a black rectangular health meter background sprite that I can fix to the camera and add to the meters group.

The next block of code does basically the same thing, but for a slightly smaller rectangle with red fill. I called this Sprite health because it represents the health of the frog, and it will be updated throughout the game.

Now take a look at just how the update occurs. In my game's main update function, I call the following function, updateHealthBar.

function updateHealthBar() {

    var m = (100 - frog.health) / 100;
    var bh = 92 - (92 * m);
    var offset = 92 - bh;

    health.key.context.clearRect(0, 0, health.width, health.height);
    health.key.context.fillRect(0, offset, 12, bh);
    health.key.dirty = true;

}

The first 3 lines just do some simple calculations to determine how tall the health meter should be and a vertical offset for it (so it looks like the bottom is 0 instead of the top). Then it gets weird. I have to access the key of my health sprite in order to get the reference to the bitmap data object. Once I have that, I can once again do some rectangle clearing and drawing commands directly on the bitmap's context. This is low-level, like drawing directly to the Canvas. But it works perfectly, and the health meter displays and functions properly in the game.

Remember, you can view the complete source code for the game on GitHub, and you can play the latest version here: http://amphibian.com/eight-bit/. And as always, don't forget to take a look at the latest comic! It's almost Christmas!

Amphibian.com comic for 14 December 2015

No comments:

Post a Comment