Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon

6. October 2014

 

 

In today’s tutorial we are going to cover the simple but powerful concept of grouping in Phaser.  As the name suggested, grouping allows you to group like minded Sprites together.  Probably easiest to jump right in with a simple example:

 

/// <reference path="phaser.d.ts"/>
class SimpleGame {
    game: Phaser.Game;
    sprite: Phaser.Sprite;
    group: Phaser.Group;
    
    constructor() {
        this.game = new Phaser.Game(640, 480, Phaser.AUTO, 'content', {
            create: this.create, preload:
            this.preload, render: this.render
        });
    }
    preload() {
        this.game.load.image("decepticon", "decepticon.png");
        
    }
    render() {

    }
    create() {
        this.group = this.game.add.group();
        this.group.create(0, 0, "decepticon");
        this.group.create(100, 100, "decepticon");
        this.group.create(200, 200, "decepticon");

        this.game.add.tween(this.group).to({ x: 250 }, 2000,
            Phaser.Easing.Linear.None, true, 0, 1000, true).start();
    }
}

window.onload = () => {
    var game = new SimpleGame();
};

 

And run it:

 

 

As you can see, all objects in the group are updated as the group is updated.  You may notice we created the sprites directly in the group using Group.create, but we didn’t have to.  We could have just as easily done:

 

    create() {
        this.group = this.game.add.group();
        var sprite1 = this.game.add.sprite(0, 0, "decepticon");
        var sprite2 = this.game.add.sprite(100, 100, "decepticon");
        var sprite3 = this.game.add.sprite(200, 200, "decepticon");
        this.group.add(sprite1);
        this.group.add(sprite2);
        this.group.add(sprite3);

        this.game.add.tween(this.group).to({ x: 250 }, 2000,
            Phaser.Easing.Linear.None, true, 0, 1000, true).start();
    }

 

You can also add groups to groups, like so:

 

    create() {
        this.group = this.game.add.group();
        this.group2 = this.game.add.group();

        this.group.create(0, 0, "decepticon");

        this.group2.create(100, 100, "decepticon");
        this.group2.create(200, 200, "decepticon");

        this.group.add(this.group2);

        this.game.add.tween(this.group).to({ x: 250 }, 2000,
            Phaser.Easing.Linear.None, true, 0, 1000, true).start();
    }

 

The above code performs identically to the earlier example.  This can provide a great way to organize your game into logical entities, such as a group for the background, a group for scrolling foreground clouds, a group for bullets, etc.

 

Grouping things together is all nice and good, but if you can’t do anything to the group, it’s mostly just pointless.  Fortunately then, there is quite a bit you can do with a group.  You can loop through them:

 

    create() {
        this.group = this.game.add.group();

        this.group.create(0, 0, "decepticon");
        this.group.create(100, 100, "decepticon");
        this.group.create(200, 200, "decepticon");

        // Set each item in the group's x value to 0
        this.group.forEach((entity) => {
            entity.x = 0;
        }, this, false);

        this.game.add.tween(this.group).to({ x: 250 }, 2000,
            Phaser.Easing.Linear.None, true, 0, 1000, true).start();
    }

 

You can sort them:

 

    create() {
        this.group = this.game.add.group();

        this.group.create(0, 0, "decepticon");
        this.group.create(100, 100, "decepticon");
        this.group.create(200, 200, "decepticon");

        // Sort group by y coordinate descending
        this.group.sort("y", Phaser.Group.SORT_DESCENDING); 
        this.game.add.tween(this.group).to({ x: 250 }, 2000,
            Phaser.Easing.Linear.None, true, 0, 1000, true).start();
    }

 

You can update a property on all group members at once:

 

    create() {
        this.group = this.game.add.group();

        this.group.create(0, 0, "decepticon");
        this.group.create(100, 100, "decepticon");
        this.group.create(200, 200, "decepticon");

        // set the alpha value of all sprites to 50%
        this.group.setAll("alpha", 0.5);

        this.game.add.tween(this.group).to({ x: 250 }, 2000,
            Phaser.Easing.Linear.None, true, 0, 1000, true).start();
    }

 

Running:

 

 

You can get the index of any item within the group:

 

    create() {
        this.group = this.game.add.group();

        var sprite1 = this.group.create(0, 0, "decepticon");
        this.group.create(100, 100, "decepticon");
        this.group.create(200, 200, "decepticon");
        this.group.sort("y", Phaser.Group.SORT_DESCENDING);

        var index = this.group.getIndex(sprite1);
        this.game.add.text(0, 0, "Sprite1's index is:" + index,
            { font: "65px Arial", fill: "#ff0000", align: "center" },
            this.group); // Index should be 2

        this.game.add.tween(this.group).to({ x: 250 }, 2000,
            Phaser.Easing.Linear.None, true, 0, 1000, true).start();
    }

 

Running:

 

 

And as you might be able to see from the above example, you can also add text objects directly to groups!

One other important concept of Groups is being dead or alive.  There are all kinds of methods for checking if an entity is alive or not, like so:

 

    create() {
        this.group = this.game.add.group();

        var sprite1 = this.group.create(0, 0, "decepticon");
        this.group.create(100, 100, "decepticon");
        this.group.create(200, 200, "decepticon");


        sprite1.alive = false;
        this.group.forEachDead((entity) => {
            entity.visible = false;
        }, this);

        this.game.add.tween(this.group).to({ x: 250 }, 2000,
            Phaser.Easing.Linear.None, true, 0, 1000, true).start();
    }

 

This kills off the first of the three sprites, leaving you:

 

 

This tutorial only scratched the surface on what groups can do.  Simply put, they are a convenient data type for logically holding your game objects.  The only thing I didn’t mention is what happens when a group doesn’t have a parent.  In this situation, that parent is the games’ World, which… is a group.

 

Programming , , ,

23. September 2014

 

 

In the previous particle tutorial I used something called a tween followed by the instructions “don’t worry about it, I will cover them later”.  Well, welcome to later!

 

First off, what exactly is a tween?  It’s actually probably exactly what you expect it is, basically it’s “between” turned into a verb.  Tweens are all about transitioning from one state to another, and they are incredibly handy.  Let’s just straight in with an example:

 

/// <reference path="phaser.d.ts"/>
class SimpleGame {
    game: Phaser.Game;
    sprite: Phaser.Sprite;
    
    constructor() {
        this.game = new Phaser.Game(640, 480, Phaser.AUTO, 'content', {
            create: this.create, preload:
            this.preload, render: this.render
        });
    }
    preload() {
        this.game.load.image("decepticon", "decepticon.png");
        
    }
    render() {
    }
    create() {
        this.sprite = this.game.add.sprite(0, 0, "decepticon");
        this.game.add.tween(this.sprite).to(
            { x: 400 }, 5000, Phaser.Easing.Linear.None, true, 0, Number.MAX_VALUE, true);
    }
}

window.onload = () => {
    var game = new SimpleGame();
};

 

This example shows a to() tween, which is it tweens between the initial value and the value you specify.  When creating a tween, you pass in the option you wish to tween, in this case a Phaser.Sprite.  The first value are the properties we wish to tween, in this cases sprite’s x value.  Next we tell it how long to take ( 5 seconds ), then pass in the easing function we want to use, in this case none ( which by the way, would have been the default value ), next is the delay before beginning ( right away ), how many times to perform ( infinite ) the tween, then finally we pass true to bounce.  Bounce says after you are done the tween, go back to the beginning.

 

And here is the result

 

 

As you can see the image moves across the screen until it’s x value is 400.

 

Now let’s take a look at how the easing function affects the tween.  Instead of no easing function, let’s apply an elastic band type affect.  We use InOut, which means it will ease at the beginning and end of the Tween.

 

    create() {
        this.sprite = this.game.add.sprite(0, 0, "decepticon");
        this.game.add.tween(this.sprite).to({ x: 400 }, 5000,
            Phaser.Easing.Elastic.InOut, true, 0, Number.MAX_VALUE, true);
    }

 

And the result:

 

 

Tweens don't have to be limited to positioning items either, you can tween just about any value. For example, let’s instead tween the alpha value of our sprite:

 

    create() {
        this.sprite = this.game.add.sprite(0, 0, "decepticon");
        this.game.add.tween(this.sprite).to({alpha:0 }, 5000,
            Phaser.Easing.Quadratic.In, true, 0, Number.MAX_VALUE, true);
    }

 

And the result

 

 

You can also chain together tweens to perform a series of transitions:

 

    create() {
        this.sprite = this.game.add.sprite(0, 0, "decepticon");
        this.game.add.tween(this.sprite)
            .to({ x: 400 }, 2000)
            .to({ y: 250 }, 2000)
            .to({ x: 0 }, 2000)
            .to({ y: 0 }, 2000).loop().start();
    }

 

Resulting in:

 

 

Tweens also have event handlers attached. Here for example is a tween that has an onStart and onComplete event handler, that fire before and after the tween completes respectively.

 

    create() {
        this.sprite = this.game.add.sprite(0, 0, "decepticon");
        var tween = this.game.add.tween(this.sprite);
        tween.to({ x: 400 });

        tween.onStart.addOnce(() => {
            this.sprite.scale.setMagnitude(0.5);
        });
        tween.onComplete.addOnce(() => {
            this.game.add.tween(this.sprite).to({ x: 0 }).start();
            },this);
        tween.start();
    }

 

Tweens provide a pretty powerful but simple way of adding actions, either in sequence or parallel, to your game’s contents.

 

Programming , , , ,

12. September 2014

 

 

In my previous Phaser tutorial on particles I used a sprite sheet to provide particles in one of the examples, then realized I hadn’t covered how to actually use a sprite sheet yet, oops!  So this tutorial is going to correct that oversight.  A spritesheet ( also known as a texture atlas ), is simply a collection of images together in a single image file.  Loading a single file into memory is often more efficient than loading dozens on small images.  It’s also generally easier from a resource management perspective.

 

Let’s take a look using the texture I used for the particle example.  It’s a seamless animation of a robin in flight that I downloaded from here.  Here is a small version of the image:

robin-782x1024

 

Now imagine the above image as a 5x5 grid of images.  This gives 22 240x314 bird images and 3 empty spaces.  Now let’s look at using that image to create an animation.

 

/// <reference path="phaser.d.ts"/>
class SimpleGame {
    game: Phaser.Game;
    bird: Phaser.Sprite;

    constructor() {
        this.game = new Phaser.Game(640, 480, Phaser.AUTO, 'content', {
            create: this.create, preload:
            this.preload, render: this.render
        });
    }
    preload() {
        // Load the spritesheet containing the frames of animation of our bird
        // The cells of animation are 240x314 and there are 22 of them
        this.game.load.spritesheet("ss", "robin.png", 240, 314, 22);
    }
    render() {
    }
    create() {
        this.bird = this.game.add.sprite(0, 0, "ss", 0);
        // Create an animation using all available frames
        this.bird.animations.add("fly");
        // Play the animation we just created at 10fps, looping forever
        this.bird.play("fly", 10, true);
    }
}

window.onload = () => {
    var game = new SimpleGame();
};

 

And running:

 

 

 

So I mentioned earlier about a texture atlas.  What is that if not a spritesheet?  Well basically it’s the same thing, but with a  table of contents if you will.  The spritesheet relies entirely on images being the same size in a grid like the above.  A texture atlas on the other hand is more flexible.

 

First is the matter of how to create them.  There are actually several applications capable of creating Phaser compatible texture atlases, but TexturePacker is probably the easiest.  I am not going to go into detail on how to use TexturePacker in this post as I have already done so in the past.  For more information click here.

 

There are a couple things to be aware of that are Phaser specific.  For this example I am going to use the animation I created in my Blender tutorial series.  It’s simply the jet in a barrel roll animation, rendered as individual frames, like so:

 

0005000100020003

 

When choosing your project type, Phaser is not listed.  Instead choose Sparrow/Starling:

image

 

Simply drag and drop your frames on animation then set the following settings:

image

 

Size Constraints does not need to be set to Power of 2, but it’s generally a good idea for performance.

Allow Rotation CANNOT be checked, it will break Phaser.

Trim mode is important for this case, as I want to keep the white spaces so the image size remains the same across all animations.  For animations you will generally want to set this to keep your frames the same size.  For non-animations, it can save a great deal of space not selecting this option.

 

When ready click Publish Sprite Sheet at the top button bar.  This will then save an xml file and a png for you to use.  I called mine jet.xml and jet.png, but I am a creative type.

 

Now let’s look at the code for using a texture atlas instead of a sprite sheet… a bit of a warning, it’s awfully same-y.

 

/// <reference path="phaser.d.ts"/>
class SimpleGame {
    game: Phaser.Game;
    jet: Phaser.Sprite;

    constructor() {
        this.game = new Phaser.Game(640, 480, Phaser.AUTO, 'content', {
            create: this.create, preload:
            this.preload, render: this.render
        });
    }
    preload() {
        this.game.load.atlasXML("jet", "jet.png", "jet.xml");
    }
    render() {
    }
    create() {
        this.jet = this.game.add.sprite(0, 0, "jet", 0);
        // Make it bigger so we can see
        this.jet.scale.setMagnitude(3);
        this.jet.animations.add("fly");
        this.jet.animations.play("fly", 15,true);
    }
}

window.onload = () => {
    var game = new SimpleGame();
};

 

And when we run it:

 

 

And that is how you use a spritesheet or texture atlas.

Programming , , , ,

5. September 2014

 

 

Other than being a word my brain seems to think is spelled incorrectly, Particles are a common part of many games.  Fortunately Phaser has pretty good support for particles.  In this tutorial we are going to take a look at how they are used.

 

First, a quick introduction to particles.  A particle system is simply a mass of things, namely particles.  A particle is often ( as in this case ) a simple sprite.  The entire idea behind a particle system is to control a large number of particles using a single controller.  Particles are commonly used to create effects like fire and smoke, where each dozens, hundreds or thousands of smoke and flame particles are emitted ( pushed ) from the particle emitter.  As the emitter moves, so do the particles emitted from it.  This allows you to define over all behavior in the emitter, not each particle.  If that makes no sense, don’t worry, the examples below should clear it up.

 

Let’s take a look at a very basic emitter.  First we need a particle graphic.  In this case I am using the following graphic:

 

particle

 

It’s a simple transparent png image named particle.png.  Now the code:

 

/// <reference path="phaser.d.ts"/>

class SimpleGame {
    game: Phaser.Game;
    sound: Phaser.Sound;
    emitter: Phaser.Particles.Arcade.Emitter;

    constructor() {
        this.game = new Phaser.Game(640, 480, Phaser.AUTO, 'content', { create: 
                    this.create, preload: this.preload});
    }
    preload() {
        // Load the image we are going to use for our particle
        this.game.load.image("particle", "particle.png");
    }
    create() {
        // Now we are creating the particle emitter, centered to the world
        this.emitter = this.game.add.emitter(this.game.world.centerX, this.game.
                       world.centerY);
        // Make the particles for the emitter to emmit.  We use the key for the 
        particle graphic we loaded earlier
        // We want 500 particles total
        this.emitter.makeParticles('particle', 1, 500, false, false);
        // And BOOM, emit all 500 particles at once with a life span of 10 
        seconds.
        this.emitter.explode(10000, 500);
    }
}

window.onload = () => {
    var game = new SimpleGame();
};

 

Now when you run this code:

 

p1

 

500 particles using our png texture “explode” out of the emitter all at once.  The code is fairly heavily commented to explain what exactly is going on so I am going to move on to the next example.

 

Some ( most? ) of the time, you arent going to want all of your particles exploding all at once, except perhaps if you are modeling an explosion that is, instead you often want them to stream over time, perhaps from a moving source.  Also, you often want to use more than a single particle graphic for variety.  Finally, you often want those particles to physically interact with the world.  This example is going to do all of this.

 

This time we are going to use multiple (poorly edited by me) image files for our particles:

particleparticle2particle3

 

And now the code:

 

/// <reference path="phaser.d.ts"/>

class SimpleGame {
    game: Phaser.Game;
    sound: Phaser.Sound;
    emitter: Phaser.Particles.Arcade.Emitter;
    sprite: Phaser.Sprite;

    constructor() {
        this.game = new Phaser.Game(640, 480, Phaser.AUTO, 'content', {
            create: this.create, preload: this.preload,
            update: this.update
        });
    }
    preload() {
        // This time we have 3 different particle graphics to use
        this.game.load.image("particle", "particle.png");
        this.game.load.image("particle2", "particle2.png");
        this.game.load.image("particle3", "particle3.png");

        this.game.load.image("logo", "gfslogo.png");
    }
    update() {
        // This checks for and handles collisions between our sprite and 
        particles from the emitter
        this.game.physics.arcade.collide(this.sprite, this.emitter);
    }
    create() {
        var start = new Date().getTime();
        for (var i = 0; i < 1e7; i++) {
            if ((new Date().getTime() - start) > 10000) {
                break;
            }
        }
        this.emitter = this.game.add.emitter(0, 0);
        // As you can see, you can pass in an array of keys to particle graphics
        this.emitter.makeParticles(['particle', 'particle2', 'particle3'], 1, 
                                   500, false, false);
        // Instead of exploding, you can also release particles as a stream
        // This one lasts 10 seconds, releases 20 particles of a total of 500
        // Which of the 3 graphics will be randomly chosen by the emitter
        this.emitter.start(false, 10000, 20, 500, true);

        // This line of code illustrates that you can move a particle emitter.  
        In this case, left to right across
        // The top of the screen.  Ignore details tweening for now if it's new, 
        we will discuss later
        this.game.add.tween(this.emitter).to({ x: this.game.width }, 10000,null,
                            true,0,1,true).start();

        // Let's create a sprite in the center of the screen
        this.sprite = this.game.add.sprite((this.game.width / 2) - 100, (this.
                      game.height / 2) - 100, "logo");
        // We want it to be part of the physics of the world to give something 
        for particles to respond to
        // Note, we will cover physics in more detail later ( after tweening 
                                                              perhaps... ;) )
        this.game.physics.enable(this.sprite);
        this.sprite.body.immovable = true;
    }
}

window.onload = () => {
    var game = new SimpleGame();
};

 

And run it:

 

p2

 

As you can see, you can provide multiple particles, move the emitter and have it physically interact with the scene.  Don’t worry over much about tweening or physics, these are two subjects we will cover later in more detail.

 

In these two examples, we’ve looked at particles like you would use for special effects work, a very common situation.  That said, Particles can actually be used to control a large number of game entities that have common over all behavior, but randomized location, rotation, size or scale.  Take for example, a flight of animated birds.  You might want to add birds flying in the background of your game.  Let’s take a look at how to do this!

 

First I am using (a full sized version) of this sprite sheet taken from here.

robin-782x1024

 

It’s an animated sequence of a robin in flight.  It contains 22 frames, each 240x314 pixels in size.

 

Now let’s take a look at the code:

 

/// <reference path="phaser.d.ts"/>


// Create our own Particle class
class MyParticle extends Phaser.Particle {
    elapsedTime: number;
    currentFrame: number;
    static MAX_FRAME: number = 22;
    game: Phaser.Game;

    // In the constructor, randomly pick a starting frame of animation so all 
    the 
    // birds arent animated at the same rate
    constructor(game: Phaser.Game, x: number, y: number, key?: any, frame?: any) 
                {
        this.currentFrame = Math.floor(Math.random() * MyParticle.MAX_FRAME);
        this.elapsedTime = 0;
        this.game = game;

        // Now call Particle's constructor, passing in the spritesheet and frame 
        to use initially
        super(game, x, y, "ss", this.currentFrame);
    }

    update() {
        super.update();

        // Ever 100ms move to the next frame of animation
        this.elapsedTime += this.game.time.elapsed;
        if (this.elapsedTime >= 100) {
            this.currentFrame++;
            if (this.currentFrame > MyParticle.MAX_FRAME) this.currentFrame = 0;
            this.frame = this.currentFrame;
            this.elapsedTime = 0;
        }
    }
}

class SimpleGame {
    game: Phaser.Game;
    sound: Phaser.Sound;
    emitter: Phaser.Particles.Arcade.Emitter;

    constructor() {
        this.game = new Phaser.Game(640, 480, Phaser.AUTO, 'content', {
            create: this.create, preload:
            this.preload, render: this.render
        });
    }
    preload() {
        // Load the spritesheet containing the frames of animation of our bird
        // The cells of animation are 240x314 and there are 22 of them
        this.game.load.spritesheet("ss", "robin.png", 240, 314, 22);
    }
    render() {
    }
    create() {
        // Now we are creating the particle emitter, centered to the world
        this.emitter = this.game.add.emitter(this.game.world.centerX, this.game.
                       world.centerY);

        this.emitter.particleClass = MyParticle;
        this.emitter.makeParticles();
        
        // In this case, we dont want gravity affecting the birds and we dont 
        want them to rotate
        this.emitter.gravity = 0;
        this.emitter.minRotation = 0;
        this.emitter.maxRotation = 0;

        // Now start emitting particles.  Total life of 1000 seconds, create one 
        per 500ms ( 2/sec )
        // and create a total of 100 birds.
        this.emitter.start(false, 100000, 500, 100, true);
    }
}

window.onload = () => {
    var game = new SimpleGame();
};

 

And when run:

 

p3

 

The terrible graphics are a side effect of being converted to an animated GIF that would take forever to download!

 

It just dawned on me that I haven’t actually covered Spritesheets yet… oops.  Hey, this is how you use a spritesheet! :)  Don’t worry if you struggle with the spritesheet portions, we will cover that later as well.  The important part here is the Particle itself.  In this example we extend the class Particle to create our own Particle class.  This class then handles updating itself resulting in the animation of the bird.  Obviously you could put a great deal more logic in there, although be sure to keep things pretty light computationally, especially if you intend to create a number of them.

 

This example actually illustrates a couple of really nice features of TypeScript, but maps nicely to Phaser’s JavaScript.  The first in the concept of a constructor.  This is code that is called as the object is created.  The next more important concept is class and extend.  In this case we are extending an existing JavaScript class, Particle.  A quick look behind the curtains now.

 

In TypeScript we write:

 

class MyParticle extends Phaser.Particle

 

This results in the JavaScript

 

var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
// Create our own Particle class
var MyParticle = (function (_super) {
    __extends(MyParticle, _super);

 

Pretty cool stuff, and certainly easier to read and maintain.

 

A Small Bug

 

While writing this I ran into a small bug I figured I should make you aware of ( and one of the downsides of working with TypeScript ).  In the code sample we had the following line:

 

this.emitter.particleClass = MyParticle;

 

This line is specifying the type of class to use for a Particle, no an instance of the class.  Unfortunately the Phaser.d.ts file is incorrect in it’s definition.  It instead expects particleClass to receive a Phaser.Particle instance.  This is wrong ( and took some time to route out ).  In the definition, if not fixed by the time you read this, make the following edit to Emitter:

 

particleSendToBack: boolean;
// MJF Fix particleClass: Phaser.Sprite;
particleClass: any;

 

Hopefully that will be fixed by the time you read this.

 

Programming , , , ,

19. August 2014

 

 

Now that we’ve covered input and graphics, it’s time we move on to audio.  Here is a bit of a warning upfront…

 

HTML5 audio sucks.

 

Yeah, that’s about it in nutshell.  This is one of those areas where games in HTML5 really suffer and sadly it doesn’t seem to be going away any time soon.  There are two major things to be aware of right up front.

 

First, audio file formats.  Different browsers support different file formats, but not all the same formats sadly.  You can read a pretty good chart on the topic right here.  In a nutshell, some support ogg, some support mp3, some support wav and some support m4a.  To make things even more fun, mp3 is a license encumbered format that could result in heavy licensing fees if your game is successful.  In reality, it’s never really enforced, but just the possibility should be enough to make you wary.  Fortunately, Phaser provides a way to deal with all of this that you will see shortly.

 

Second, well, Safari on iOS really kinda stinks at audio for a couple glaring reasons.  First and foremost, you can only play one sound or video at a time…  Yeah, really.  If you play a second sound, the first immediately stops.  Kinda. Big. Deal.  You can however work around this using an audio sprite, which is basically a bunch of audio files smashed together into a single file.  A second major failing is you can’t play audio on page load in Safari.  This means if you want to load your game up to a title screen with some music playing… you cant.  Audio can only be started in response to a users touch.  Yeah, this sucks too.  This one unfortunately you cannot work around, short of using a wrapper like CocoonJS for “natively” deploying your HTML5 game.

 

Through this all, you are probably going to need a tool to either merge your audio, or convert to a variety of formats.  Fortunately there is a free and excellent audio editor named Audacity available, that makes the process fairly painless.  In order to follow this tutorial, you are going to have to get an audio file and save it in mp3 and ogg format and add it to your project.

 

OK, enough about the HTML5 audio warts, let’s get to some code! 

 

/// <reference path="phaser.d.ts"/>
class SimpleGame {
    game: Phaser.Game;
    sound: Phaser.Sound;

    constructor() {
        this.game = new Phaser.Game(640, 480, Phaser.AUTO, 'content', { create: this.create, preload: this.preload });
    }
    preload() {
        this.game.load.audio("GameMusic", ["song.mp3","song.ogg"]);
    }
    create() {
        this.sound = this.game.add.audio('GameMusic');
        this.sound.play();
    }
}

window.onload = () => {
    var game = new SimpleGame();
};

 

It’s important to note, this example wont actually work in Safari on iOS due to the limitation of only being able to play audio in response to a user touch.  You would have to change it so the play() call is done in a touch handler.

 

Here what you are doing is preloading audio using game.load.audio().  The parameters are the key to refer to the audio file, and a list of files to load.  This is where the magic happens, you should provide all supported file formats, then Phaser will serve the one that performs best on the browser the user is using.  M4A files perform the best on iOS, and don’t have the legal encumbrance that MP3, so OGG and M4A would probably be all you needed, but I may be wrong here.  If in doubt, provide all 3.

 

Now let’s look at an example that allows you to control playback of the sound file:

 

/// <reference path="phaser.d.ts"/>
class SimpleGame {
    game: Phaser.Game;
    sound: Phaser.Sound;
    playButton: Phaser.Button;
    pauseButton: Phaser.Button;
    stopButton: Phaser.Button;
    volUpButton: Phaser.Button;
    volDownButton: Phaser.Button;
    muteButton: Phaser.Button;

    constructor() {
        this.game = new Phaser.Game(640, 480, Phaser.AUTO, 'content', { create: this.create, preload: this.preload, 
render
: this.render }); } preload() { this.game.load.audio("GameMusic", ["song.mp3", "song.ogg"]); this.game.load.image("button", "button.png", false); } render() { this.game.debug.soundInfo(this.sound, 0, 100); } create() { this.sound = this.game.add.audio('GameMusic'); // Set up sound event handlers on the sound object this.sound.onPlay.add(() => { alert("Played"); }); this.sound.onPause.add(() => { alert("Paused"); }); // Play Button this.playButton = this.game.add.button(0, 0, "button", () => { if (this.sound.currentTime > 0) this.sound.resume(); else this.sound.play(); } , this); this.playButton.addChild(new Phaser.Text(this.game, 17, 18, "Play", { fill: '#ff0000' })); // Pause Button this.pauseButton = this.game.add.button(95, 0, "button", () => { this.sound.pause(); } , this); this.pauseButton.addChild(new Phaser.Text(this.game, 12, 18, "Pause", { fill: '#ff0000' })); // Stop Button this.stopButton = this.game.add.button(190, 0, "button", () => { if (this.sound.isPlaying) { this.sound.stop(); this.sound.currentTime = 0; } } , this); this.stopButton.addChild(new Phaser.Text(this.game, 17, 18, "Stop", { fill: '#ff0000' })); // Volume Up Button this.volUpButton = this.game.add.button(300, 0, "button", () => { this.sound.volume += 0.1; } , this); this.volUpButton.addChild(new Phaser.Text(this.game, 17, 18, "Vol +", { fill: '#ff0000' })); // Volume Down Button this.volDownButton = this.game.add.button(400, 0, "button", () => { this.sound.volume -= 0.1; } , this); this.volDownButton.addChild(new Phaser.Text(this.game, 17, 18, "Vol -", { fill: '#ff0000' })); // Mute Button this.volDownButton = this.game.add.button(500, 0, "button", () => { // Global mute! Use this.sound.mute to mute a single sound this.game.sound.mute = !this.game.sound.mute; } , this); this.volDownButton.addChild(new Phaser.Text(this.game, 17, 18, "Mute", { fill: '#ff0000' })); } } window.onload = () => { var game = new SimpleGame(); };

 

Here is the resulting app:

 

 

It’s a bit of a monster, but most of the code is actually just wiring up the buttons.  There are a few important take away points here.  First, you can work with the local sound object, or all sounds globally using game.sound.  Second, each action on the sound file ( play, resume, stop, etc ) have an appropriate event handler you can implement.  I only did a couple in this example.  Finally, volume is a value from 0 to 1.  You can go above or below this range, but it wont actually do anything.  All said, once you get over the file format issues, playing audio is relatively straight forward.

 

Finally, let’s touch on audiosprites again for a second.  As mentioned earlier, an audiosprite is actually a bunch of audio files smashed together into a single file.

 

Consider the following sound effects.  A gun cocking ( http://www.freesound.org/people/woodmoose/sounds/177054/ ) and a gun shot ( http://www.freesound.org/people/18hiltc/sounds/228611/ ).  Load your first sound effect into audacity, like so:

image

 

Take note of the end of the file, in this case 0.785 seconds.

 

Now, select File->Import-> Audio

image

 

Import your additional sound effects, in this case I’m opening the gunshot file.  You should now have two tracks in Audacity, like so:

image

 

Now select the bottom track by double clicking.  It should highlight in gray, like so:

image

Now click at the end of the first track and paste ( CTRL + V, or EDIT->Paste ).  You should now have a single track that looks like this:

image

 

Now save this file ( might as well create OGG, MP3 and M4A versions while you are at it ).

 

Now lets take a look how we use it in code.

 

/// <reference path="phaser.d.ts"/>
class SimpleGame {
    game: Phaser.Game;
    sound: Phaser.Sound;

    constructor() {
        this.game = new Phaser.Game(640, 480, Phaser.AUTO, 'content', { create: this.create, preload: this.preload, 
render
: this.render }); } preload() { this.game.load.audio("sfx", ["GunSounds.ogg", "GunSounds.wav"]); } render() { this.game.debug.soundInfo(this.sound, 0, 100); } create() { this.sound = this.game.add.audio('sfx'); this.sound.addMarker("gunCock", 0, 0.785); this.sound.addMarker("gunShoot", 0.786, 1.49); this.sound.play("gunCock"); this.sound.onMarkerComplete.add(() => { this.sound.play("gunShoot"); }); } } window.onload = () => { var game = new SimpleGame(); };

 

This plays the first sound, then immediately once it is complete, it plays the other.  As you can see, you do so by setting markers within the entire sound file.  0.785 is the length of the first sound effect, then 1.49 is the length of the entire file.  You simply name each marker, then give it’s start and end locations within the file.  You can get these values using an audio editor, such as audacity.  This allows you to load all of your similar sound effects into a single quicker to load file and should help you work around the single sound stream limitations of iOS.

 

Programming , , , ,

Month List

Popular Comments