This is the home stretch, but also a good significant chunk of code — particularly in this post. It’s time to create the game mechanics that matter: combat.

We’re going to be covering some new ground here, and it’s the first of many references to object-oriented programming (or “OOP” for short). I’ll explain as we go along, but don’t forget: it’s okay if these concepts don’t immediately stick with you.

LET’S BOOGIE!

npm start

First thing’s first: open VSCode, open the Console (click View > Integrated Terminal) if it’s not already open, and run npm start.

VSCode
Time to make the chimichangas.

(Digital) Bullets for Everyone!

Hop into invaders.js, and let’s create some variables!

// This is our collection of player bullets.
var bullets;

// This is our collection of alien bullets.
var enemyBullets;

// This is a single bullet instance.
var Bullet = new Phaser.Class({

});

Don’t worry. I’ll address that last variable in just a minute. First, let’s also make sure we’re preloading our two bullet images in the handlePreload function:

// HOOK, PART 1: The preload hook.
function handlePreload() {

    // ... previous code ...

    // Preload the player bullet image.
    this.load.image( 'bullet', 'bullet.png' );

    // Preload the enemy bullet.
    this.load.image( 'enemyBullet', 'enemy-bullet.png' );
}

You should end up with something like this.

Variables and image preloading
Takin' care of business...

Phaser.Class

Alright, let’s address the elephant in the room: that weird Phaser.Class variable. We left it open because we’re going to be putting some stuff in there, but before we get to that I’d like to discuss what a class is in the context of programming.

The super-abbreviated analogy is to think of a class like a set of blueprints for a house. Blueprints define the shape, building materials, functionality, and layout of a house, right? Right. That’s what a class does, and they’re also very similar in that the blueprints are merely instructions for building the house; it’s not the house itself. A class — in this case Bullet — is just the instructions for building an a single bullet in our game, but it’s not the bullet itself.

If you recall the concept of objects from my previous post — JavaScript 101 — we could probably do everything we need to without getting into using a class, so why do it here?

When we use a class we can write more efficient code. One way that we do this is by way of something that we call inheritance. When one class inherits another, it basically becomes a child of that class, and inherits all of the parent’s values and functionality. That’s something that we want for our game because every bullet that appears on screen should not only be a Phaser image, but also have some extra functionality that is specific to one of our game bullets.

Let’s update our Bullet class variable.

// This is a single bullet instance.
var Bullet = new Phaser.Class({
    Extends: Phaser.GameObjects.Image,

    initialize: function Bullet( scene, x, y, defaultKey ) {

    },

    fire: function( shooter, target ) {

    },

    update: function( time, delta ) {

    }
});

Notice the Extends: Phaser.GameObjects.Image line? We’re saying that this class is going to inherit the basic properties and functions of Phaser.GameObjects.Image, and that means that every instance of Bullet that we create in the game will be recognized as an image by Phaser.

Then we come to a few functions.

initialize

This will be automatically called by Phaser anytime a new instance of Bullet is created. See how we named the function that initialize uses? Under the hood, Phaser is going to call new when it runs initialize, so we’re giving it the name of our class to keep things straight. When a specific function is called to create a new instance of a class, we call that a constructor. We use that function to setup the bare minimum things that the instance needs. While initialize will automatically be called by Phaser, we only need to include this function in our Phaser classes if we need to setup things specific to our game. We do, so we’re including it.

Since we inherited from Phaser.GameObjects.Image, we need to make sure that our initialize constructor function takes the same parameters as the parent.

Phaser.GameObjects.Image takes many parameters, but only the first three are required. Since we’re going to use this class for both types of bullets — player and alien invader — we need to also use the fourth parameter in order to tell Phaser to use the correct preloaded image based on which type we’re creating.

fire

This is a custom function for our class, which means that it won’t be used by Phaser automatically. We’ll have to call it manually when the time is right, but that’s okay. This function is going to define what happens to a single bullet when it’s been fired from somewhere.

We need to know the position of both who fired it (so we know where on the screen it should appear to be fired from), and where it’s going. Thus, our fire function takes two parameters: shooter and target.

update

This is another function — like initialize — that Phaser will fire on its own. We’re going to use this function to actually move the bullet, much like we do with our starfield sprite in the scene’s handleUpdate function.

We’re also going to automatically remove the bullet from the collection of things that Phaser has to keep track of if the bullet misses its target and leaves the scene. Otherwise, the bullet would just keep traveling — even though we can’t see it anymore — and Phaser would have to keep paying attention to it — even though its completely useless at that point.

It takes a few different parameters, but we’re primarily concerned with two: time and delta. time is the current time according to Phaser’s internal object tracking clock. delta is the difference in time between the last time that update was called and now.

The structure of our bullet class.
The structure of our bullet class.

Base Properties of a Bullet

Our game needs to know a few basic things about every bullet that’s fired.

  1. Whether the bullet was fired by an alien invader, or the player.
  2. Its general traveling speed.
  3. The direction it’s traveling.
  4. Its specific speed along the x-axis.
  5. Its specific speed along the y-axis.
  6. The “game time” that it was fired.

Since we need to keep track of these, we need to set them up in our initialize constructor function. At the same time, we also need to tell Phaser to use our preloaded image for the bullet, and because we have separate images for the player bullet and invader bullet, we have to make sure that the image size is set correctly.

// This is a single bullet instance.
var Bullet = new Phaser.Class({
    Extends: Phaser.GameObjects.Image,

    initialize: function Bullet( scene, x, y, defaultKey ) {
        // Tell Phaser to use our preloaded image.
        Phaser.GameObjects.Image.call( this, scene, 0, 0, defaultKey );

        // Is this a bullet for the enemy?
        this.isEnemyBullet = ( defaultKey === 'enemyBullet' );

        // This is our base speed.
        this.speed = 0.1;

        // This is the time when the bullet was "born" (fired).
        this.born = 0;

        // This is the direction the bullet is traveling.
        this.direction = 0;

        // This is the bullet's speed along the x-axis.
        this.xSpeed = 0;

        // This is the bullet's speed along the y-axis.
        this.ySpeed = 0;

        // Set the image size based on the type of bullet.
        if ( this.isEnemyBullet ) {
            this.setSize( 9, 9, true );
        } else {
            this.setSize( 6, 36, true );
        }
    },
  
    // ... fire function ...
  
    // ... update function ...
});

Phew! That was a lot! The first thing we’re doing is telling Phaser to actually create the image object for the bullet. It looks a little weird because we’re using the function call, and that’s because we’re actually inside of the very thing we’re trying to create (we inherited from it, remember?), so we’re basically saying, “Phaser, create the parent of the Bullet class.”

We know from previous usage of Image that it takes parameters of: the scene it should be added to, the x- and y-coordinates where it should appear, and the unique name given to the preloaded asset. When we use the call function of an inherited parent in JavaScript, the first parameter is always a reference to the child — the object that’s inheriting from the parent. Since we’re actually in the child at this point, this means child. We’re telling Phaser, “I’m creating a child of Image right now, so go ahead and do your thing, but apply it to this Image.” Then we’re passing the usual arguments, but since the bullet hasn’t yet been fired, we tell Phaser to temporarily store it at the x- and y-coordinates of 0, 0. Then we pass in the unique name of the preloaded image, which we don’t know yet, but will be passed in later as the parameter named defaultKey.

Next, we start setting our base properties for a bullet, starting with whether this bullet was fired by an enemy or the player. Since we only need to know whether it’s one of two possibilities, we use a simple boolean. If it’s not an enemy bullet, then the only other thing it can be is a player bullet.

You’ll notice a couple of odd things in this line. Let’s take a look:

this.isEnemyBullet = ( defaultKey === 'enemyBullet' );

This seems like a strange way to set true or false, because previously we’ve only seen a boolean value set using if/else statements. This is a shortcut, and the browser will evaluate — meaning “get the result” — everything between the parentheses. When it does, it’ll see the multiple = signs, and know that we’re checking if something is true.

How about that ===? We could use == or === in this case. The extra = means “be super strict when you’re checking if this is true.” Here’s an example:

var isTrue = ( 1 + 1 == 2 );    // Output: true
var isTrue = ( 1 + 1 == '2' );  // Output: true
var isTrue = ( 1 + 1 === 2 );   // Output: true
var isTrue = ( 1 + 1 === '2' ); // Output: FALSE

When we use == JavaScript only does a basic check, and ignores things like the type of the value. So 1 + 1 can either be the number 2 or the string "2". If we want to also make sure that the result is strictly a specific type — like a number — we use ===. It basically says, “Is 1 + 1 equal to 2, and is 2 a number?”

We’re setting our base speed to 0.1. This isn’t going to change, but we’re going to use it later to calculate the bullet’s x- and y-axis speeds.

born, direction, xSpeed, and ySpeed are all set to 0. Why? Because the bullet hasn’t been fired yet, so we don’t know those values yet.

Finally, we’re setting the size of the preloaded image on the canvas based on whether this is an enemy bullet. The two images that we’re using — bullet.png and enemy-bullet.png — are two different sizes, which is why this is necessary.

Initialization of a bullet.
Initialization of a bullet.

Firing a Bullet

Before we write any code we have to make some game design decisions. We want to make our game easy to play, and for our player to feel challenged. We can accomplish both by not allowing the player to aim. When they press the space bar to fire, the bullet will only travel in one direction in a straight line: up.

To add some additional challenge, we’re going to allow the alien invaders to aim. They’ll be able to fire their bullets at an angle, depending on where the player is at the instant that they fire.

Get ready for some maths. We’re going to use three trigonometry functions to calculate our alien bullet trajectories: arctangent, and sine/cosine.

The arctangent function will let us calculate the angle of travel between the alien invader firing the bullet, and the player’s current position.

The sine and cosine functions will let us calculate how fast the bullet should move along the x- and y-axis while maintaining the base speed along the angle of travel.

Let’s update our Bullet’s fire function like so:

// This is a single bullet instance.
var Bullet = new Phaser.Class({
    Extends: Phaser.GameObjects.Image,

    // ... initialize function ...

    fire: function( shooter, target ) {

        // Set the starting x- and y-coordinates to the shooter's.
        this.setPosition( shooter.x, shooter.y );

        // Set things assuming that the player is shooting.
        this.direction = 90;
        this.xSpeed = 0;
        this.ySpeed = -this.speed;
        this.born = 0;

        // But if an alien is shooting, reset those.
        if ( this.isEnemyBullet ) {

            // Calculate the direction.
            var xDifference = target.x - this.x;
            var yDifference = target.y - this.y;
            this.direction = Math.atan( xDifference / yDifference );

            // Calculate the x-axis speed against the direction.
            this.xSpeed = this.speed * Math.sin( this.direction );

            // Calculate the y-axis speed against the direction.
            this.ySpeed = this.speed * Math.cos( this.direction );

            // Calculate a rotation for an enemy bullet.
            this.rotation = Phaser.Math.Angle.Between(
                shooter.x,
                shooter.y,
                target.x,
                target.y
            );
        }
    },

    // ... update function ...
});

We start off pretty basic by setting the bullet’s x- and y-coordinates to be the same as whoever’s shooting by using setPosition on this. Remember, this means “this particular bullet”.

From a code readability standpoint, we could write the next part in a few different ways. I opted for this approach because we only have to do a lot of special calculations if it’s an enemy bullet. So we basically start off assuming that the player is firing, and we set the direction, xSpeed, ySpeed, and born properties accordingly.

direction is 90, because a player bullet can only travel “up”. xSpeed is 0, because a player bullet can’t travel at an angle, which means the only speed we care about is ySpeed. Notice that the player bullet ySpeed is set to a negative number. This is important. Phaser references things spatially from the top-down. So if an object is moving from the top of the scene to the bottom of the scene, we’d use a positive number. If an object is moving from the bottom to the top, it needs to be negative. Finally, we’re setting born to 0, and we’ll get into this in the Bullet’s update function.

Now we get to the fun stuff. If this particular bullet was fired by an alien invader, we have to change the direction, xSpeed, ySpeed, and set a rotation.

We calculate the enemy bullet’s direction by first getting the difference between the target and bullet’s x-coordinate, followed by the same for the y-coordinate. We then can set direction by using JavaScript’s arctangent function (Math.atan) using the result of dividing our x- and y-coordinate values. It’s a little complicated, but that’s okay. Again, just seeing and knowing that these functions exist in JavaScript will be helpful, even if you don’t quite understand how they work yet.

Next, we calculate the speed at which the enemy bullet will travel along the x- and y-axis. We have to calculate this because we want the bullet to appear to be traveling at a consistent speed no matter what angle it’s traveling on. If we didn’t calculate this, then some bullets would appear to be moving really fast while others would be really slow. To the player, they should all appear to be moving at the same speed, which requires math!

In both cases, we multiply the positive (since we know that the invaders are firing in a downward direction) base speed against a trigonometry function. To calculate the x-axis speed, we multiply the base speed against the sine of our calculated direction. For the y-axis speed, we multiply the base speed against the cosine of our calculated direction.

Finally, we rotate the bullet visually using a nifty helper function provided by Phaser. We don’t actually need this because the bullet image would look the same no matter which way it was rotated. I’m including it nonetheless, because it’s something that we’d commonly need in other similar Phaser games. Phaser.Math.Angle.Between calculates the difference in angle between one set of x- and y-coordinates and another set of x- and y-coordinates.

Here’s our fire function in VSCode:

Firing a bullet.
ALL THE MATHS!

Moving the Bullet on update

This part’s easy. Every time the Bullet’s update function is called — around 60 times every second — we need to move it on the screen. We’re also going to set its born property, and then check whether the bullet is too old. If it is, we need to stop showing it on the screen, and tell Phaser to stop tracking it.

// This is a single bullet instance.
var Bullet = new Phaser.Class({
    Extends: Phaser.GameObjects.Image,

    // ... initialize function ...

    // ... fire function ...

    update: function( time, delta ) {

        // Set this bullet's x-coordinate.
        this.x += this.xSpeed * delta;

        // Set this bullet's y-coordinate.
        this.y += this.ySpeed * delta;

        // Update this bullet's born time.
        this.born += delta;

        // If it's more than 5,000 milliseconds, it's off the screen.
        // Remove it from the game, so Phaser doesn't have to track it anymore.
        if ( this.born > 5000 ) {
            this.setActive( false );
            this.setVisible( false );
        }
    }
});

The first two lines are pretty self-explanatory: we use the xSpeed or ySpeed, multiplied against the delta — the difference in time between when update was last called and now — to set the x- and y-coordinates of the bullet on the screen.

Then we add delta to the bullet’s born property, so the longer it stays on screen (and the more times update is called) the “older” it gets.

Finally, we’re checking to see whether the bullet is too old. In this case, 5,000 milliseconds. If the bullet is older than 5,000 milliseconds, it’s had enough time to travel out of our scene. Since there’s nothing out there for it to hit, we don’t have a use for it anymore. When this happens, we get rid of it by telling Phaser to make it inactive (setActive( false )) and invisible (setVisible( false )).

Moving the bullet
"I don't feel a millisecond older than 4,500."

Bullet Collections

Earlier, we created two variables for bullets and enemyBullets. These are going to be groups — just like our group of aliens back in Let’s Make a Game: Invaders (Part 3).

And just like how we created our group of alien invaders, we’re going to offload the creation of our bullet groups to a new function. In fact, we’re going to add it beneath that createAliens function, like this:

// ... createAliens function ...

// This will create a collection of bullets.
function createBullets( imageName, sceneRef ) {
    return sceneRef.physics.add.group({
        classType: Bullet,
        defaultKey: imageName,
        runChildUpdate: true
    });
}

The createBullets function needs to be useful to both the player’s bullet group and the alien invaders’ bullet group. How do we do that? Easy! We’ll have the function return with what we need!

The first parameter is called imageName, and it will be pass the unique preloaded image name that we want to use for this group.

The second parameter is called sceneRef, and this is important because when we call createBullets from our handleCreate function, we’ll lose our reference to scene (this inside handleCreate). We need that reference in order to associate the group with the scene, hence the parameter.

We still have to wire this up, but let’s take a look at VSCode.

createBullets function
A function to create groups of bullets.

Let’s wire them up! Inside our handleCreate function, just beneath our createAliens() call, let’s add:

// HOOK, PART 2: The create hook.
function handleCreate() {

    // ... previous code ...

    // Create the player and alien bullet collections.
    bullets = createBullets( 'bullet', this );
    enemyBullets = createBullets( 'enemyBullet', this );

    // ... code for cursors ...
}

Super easy, right? We’re just calling our function, passing in which variable we want to be given a value, the unique name of the preloaded image asset to use, and a reference to the scene. Since we’re inside handleCreate on the Phaser train here, the scene reference is called this.

bullet groups created
Assigning our bullet group variables a value.

Randomly Firing Invaders

We want a random alien invader to fire a bullet at the player periodically, and to do that we need to create a couple of new variables. Just beneath our enemyBullets variable declaration, add:

// This is our collection of alien bullets.
var enemyBullets;

// This is the last time an alien invader fired a bullet.
var lastAlienBulletTime = 0;

// This is a collection of living alien invaders.
var livingAliens = [];

As you can see by the code comments, we’re tracking the last time any alien invader fired a bullet, as well as a collection of alien invaders that haven’t been shot yet. A destroyed alien invader can’t fire a bullet, right? Right.

Now we’re going to write a new function to handle this, since we have a lot of logic to take care of, so splitting this off into its own function makes a lot of sense.

Just beneath our createBullets function:

// This will periodically fire a bullet from a random alien invader.
function fireEnemyBullet( player, time, sceneRef ) {

    // Grab the first bullet in the group, activate it, and make it visible.
    var enemyBullet = enemyBullets.get().setActive( true ).setVisible( true );

    // Find out how many alien invaders are still "alive," and track them.
    livingAliens = aliens.getChildren().filter( alien => alien.active === true );

    // If we have an instance of enemyBullet, AND there are aliens still alive.
    if ( enemyBullet && livingAliens.length > 0 ) {

        // Get a random number between 0 and the number of aliens alive.
        var randomAlienNumber = Phaser.Math.RND.integerInRange(
            0,
            livingAliens.length - 1
        );

        // Get the alien from the collection with that number.
        var randomAlien = livingAliens[ randomAlienNumber ];

        // If this alien hasn't fired in the last 4,000 milliseconds...
        if ( time - randomAlien.lastFired > 4000 ) {

            // Set the lastFired, so the alien doesn't fire again for a while.
            randomAlien.lastFired = time;

            // FIRE ZE BULLET!
            enemyBullet.fire( randomAlien, player );

            // TODO: COLLISION PHYSICS GO HERE!

            // Update the global last fired time, and add 2,000 milliseconds.
            lastAlienBulletTime = time + 2000;
        }
    }
}

Most of this will be pretty straightforward, but I’m going to call out a couple of things.

First, we’re grabbing an available bullet from the enemyBullets group, activating it, and making it visible — all in the same line.

Next, we’re assigning our livingAliens variable a value by filtering the aliens group for only those that are still active. That value basically says: “Loop through the items from the aliens group, and for each one check to see if it’s still active. If it is, give it to me. If it’s not, ignore it.”

After that, the real fun begins. If we were able to get a valid bullet from the enemyBullets group, and if there are aliens still alive to shoot at the player, then do some processing.

That processing starts by generating a random number between 0 and the number of aliens still alive minus 1. Remember, JavaScript item counting starts at 0 for arrays, so if there’s 10 items in an array (we check this using livingAliens.length) the first item is 0 and the last item is 9. We then use that random number to get the alien at that position from the livingAliens array.

The next part of processing only continues if the random alien hasn’t fired in the last 4 seconds. If that’s the case, we update the random alien to not fire again for a while. Then we call our bullet’s fire method from the big Bullet class we wrote earlier.

I left a little TODO comment in here as a reminder for the next part.

Finally, we update our lastAlienBulletTime, so we know when the last time any alien invader fired a bullet.

This was a big one, right? Here’s how that looks in VSCode.

fireEnemyBullet function
Not very coordinated, are they?

Save your invaders.js if you haven’t in a while!

Now, to actually make use of that random firing function. We need to modify the handleUpdate function slightly. We’re going to give it a parameter called time, and then use the value that’s passed in to check whether that time is greater than the last time any alien invader fired a bullet.

Like so:

// HOOK, PART 3: The update hook.
function handleUpdate( time ) {
    
    // ... previous code ...
    
    // If the invaders haven't fired a shot recently, fire one.
    if ( time > lastAlienBulletTime ) {
        fireEnemyBullet( player, time, this );
    }
}

You can see right away why we needed that extra parameter.

Here it is in VSCode:

Randomly fire a bullet
Randomly firing an alien invader bullet.

Save your progress, and check out your browser! They’re shooting at you! Buuuut… nothing happens yet. We’ll get to that shortly.

First…

Firing Back!

The player needs to be able to fire back, right?! We’re going to wire this up to the scene in another function, and much of this is going to be very similar to our random alien invader firing mechanism.

First, let’s create another variable to track the last time the player fired a bullet. We want to limit the player to one bullet every second.

// This is a collection of living alien invaders.
var livingAliens = [];

// This is the last time the player fired a bullet.
var lastPlayerBulletTime = 0;

Then let’s write our new function just below fireEnemyBullet:

// This will handle the user firing a bullet.
function firePlayerBullet( sceneRef ) {
    sceneRef.input.keyboard.on( 'keydown_SPACE', () => {

        // If the player died, no processing!
        if ( player.active === false ) {
            return;
        }

        // Grab the first bullet in the group, activate it, and make it visible.
        var playerBullet = bullets.get().setActive( true ).setVisible( true );

        // As long as we have a valid bullet, fire it.
        if ( playerBullet && sceneRef.time.now - lastPlayerBulletTime > 1000 ) {

            // We don't need a target, since we don't need to calculate angles.
            playerBullet.fire( player );

            // TODO: COLLISION PHYSICS HERE!
          
            // Update the player last fired time.
            lastPlayerBulletTime = sceneRef.time.now;
        }
    }, sceneRef );
}

We’re passing in sceneRef as a parameter again, because we need to tell the scene to listen for keyboard events. Specifically, we’re listening for a keyboard event called “keydown_SPACE”. Any time that the space bar is pressed, additional processing happens.

First, we check whether the player is alive or not. Just like a dead alien invader can’t fire a shot, neither can a dead player! We use return here to tell Phaser, “We’re done. Don’t go any further.”

Then we attempt to grab a bullet from our bullets group, activate it, and make it visible on the screen.

If we have a valid bullet, and if the player hasn’t fired a bullet in the last second, then call the fire function on our bullet. Since the player can’t target an alien invader, we don’t need to pass a target to the fire function.

I included another comment as a reminder for collision handling — we’ll be getting to that next — before finishing up by updating the lastPlayerBulletTime, so the player won’t be able to fire again for another second.

Take special note of the very last line there: }, sceneRef ); which is our way of telling Phaser that it needs to bind the keyboard event to the scene.

firePlayerBullet function
Keyboard binding, and player-firing!

Head back up to the handleCreate function, and add the following after our cursors variable assignment:

// We only want the cursor keys (arrows).
cursors = this.input.keyboard.createCursorKeys();

// Wire up the player's firing mechanism.
firePlayerBullet( this );

And in VSCode:

All wired up
All wired up!

Save invaders.js, and have a look at your browser. Use your arrow keys to move around, and press the spacebar on your keyboard. It’s magic!

Pew, Pew, Kaboom!

Last step for this post! Woo! It’s time to make things go boom when hit by a bullet, and we’ll start off by creating a group of explosions.

Just beneath our lastPlayerBulletTime variable declaration, add:

// This is the last time the player fired a bullet.
var lastPlayerBulletTime = 0;

// This is a collection of explosions.
var explosions;

Explosions variable
Just a collection of explosions. No big deal.

Moving right along, let’s preload our explosion image in the handlePreload function, being careful to load it as a spritesheet just like we did for the alien invaders.

// HOOK, PART 1: The preload hook.
function handlePreload() {
  
    // ... previous code ...
  
    // Preload the explosion image.
    this.load.spritesheet( 'kaboom', 'explode.png', {
        frameWidth: 128,
        frameHeight: 128
    });
}

Explosion spritesheet
Note the frameWidth and frameHeight!

We need to create a small group of explosions — approximately 30. There are only 30 aliens, and in the final game the player will only have 3 lives before the game is over. By limiting the number of explosions, we’re giving Phaser less items to keep track of which helps the game run a bit more efficiently. Let’s go ahead create that group as the assigned value of our explosions variable. This will go into our handleCreate function just below our bullets creation.

enemyBullets = createBullets( 'enemyBullet', this );

// Create some explosions!
explosions = this.add.group({
    defaultKey: 'kaboom',
    maxSize: 30
});

We’ve created groups before, but they’ve always been attached to the scene’s physics object. Since our explosions are really just something to display, we can add it directly to the scene.

We’re only providing a defaultKey — the unique name we gave to the preloaded image asset — and a property called maxSize, which instructs Phaser to only create up to 30 explosion spritesheets for the group.

30 explosion spritesheets
Is it possible to have too many explosions?

Now we need to create collision handlers, which are just functions that Phaser will automatically call when one object intersects another. We’re going to write three functions: one for a bullet colliding with the player, one for a bullet colliding with an alien invader, and the last one will hold the code that both of those handlers need to use.

We’re actually going to start with that last one. Just beneath the firePlayerBullet function, add the following:

// This will handle a bullet colliding with a player or alien.
function handleCollision( target, bullet ) {

    // If both the target and bullet are active.
    if ( target.active === true && bullet.active === true ) {
        
        // Deactivate the bullet, and take it off the screen.
        bullet.setActive( false ).setVisible( false );

        // Get the first explosion, and activate it.
        var explosion = explosions.get().setActive( true );

        // Place the explosion on the screen, and play the animation.
        explosion.setOrigin( 0.5, 0.5 );
        explosion.x = target.x;
        explosion.y = target.y;
        explosion.play( 'explode' );
    }
}

We’re just making sure that both the target — the player or an alien invader — are active, along with the bullet that collided with them. If both are active we immediately deactivate the bullet, and remove it from the screen.

Then we grab the first explosion from the pile, and activate it.

Finally, we place it on the screen using our target’s x- and y-coordinates, and tell Phaser to play our animation.

Generic collision handler
Both the alien and player collision handling will use this function.

Beneath the handleCollision function, let’s create another one to handle what happens when the player is hit by an alien invader bullet:

// This will handle a bullet colliding with the player.
function handlePlayerCollision( player, bullet ) {

    // If both the player and bullet are active...
    if ( player.active === true && bullet.active === true ) {

        // Fire the generic collision handler.
        handleCollision( player, bullet );
    }
}

Easy peezy. We’re just ensuring that the player and bullet are both active, and then firing the handleCollision function that we just made.

Player collision
Fly faster next time.

One last function, and this time for when an alien invader is hit by a player bullet. Put this just below handlePlayerCollision:

// This will handle a bullet colliding with an alien.
function handleEnemyCollision( bullet, alien ) {
    if ( bullet.active === true && alien.active === true ) {

        // Fire the generic collision handler.
        handleCollision( alien, bullet );

        // Deactivate and remove the alien from the screen.
        alien.setActive( false ).setVisible( false );
    }
}

Same deal as last time, except we’re also removing the alien that was hit by the bullet. Why didn’t we do that for the player? You’ll see in the next post.

Enemy collision
Cue Will Smith: "Welcome to Earth"

The final two steps for this post are also the easiest. We have to wire these functions up in the right spots, and if you recall those TODO comments from earlier, you have an idea of what we’re doing next.

Inside our fireEnemyBullet function, replace that TODO line with:

// Setup collision handling.
sceneRef.physics.add.collider( player, enemyBullet, handlePlayerCollision );

In VSCode:

Wiring up the player collision
Player, meet bullet.

And then inside our firePlayerBullet function, replace the TODO line with:

// Setup collision handling.
sceneRef.physics.add.collider( aliens, playerBullet, handleEnemyCollision );

And in VSCode:

Wiring up the alien collision
Alien invader, meet bullet.

Save invaders.js! DO IT NOW!

After saving, check out your browser window. Bask in the glow of your awesomeness! BLOW UP SOME ALIENS!

Only one post left, folks! Next time, we tackle the end-game: scoring, player deaths, and restarting the game!

You can find the source code for this series at GitHub.