We’re reaching the halfway point here, folks! In this post, we’re going to cover quite a bit of ground:

  1. Add our playable “hero” to the game.
  2. Make our player controllable using the left and right arrow keys.
  3. Create all of our alien invader “bad guys”.
  4. Animate our alien invaders, so they sort of hover.

Though that sounds like quite a bit, this will actually be a fairly short post. There’s really only a few new concepts over the previous parts that I’ll call out, and we’re going to make good use of the JavaScript for loop that we discussed in JavaScript 101 to make things easier for ourselves when creating our alien invaders.

ONWARD!

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.

That’ll launch our browser window, so we can see things running as we progress. Professional engineers use this and other tools to do what we call rapid prototyping. As we take on other projects besides this game, I’ll speak a bit more to that, but as you’ll see throughout this post it’s a very handy tool to have.

Browser open and running Invaders.
Just sorta keep it behind VSCode like this.

Creating Our “Hero”

Open invaders.js in VSCode. We need to declare a variable for our player, just like we did for the background in Let’s Make a Game: Invaders (Part 2). In fact, just beneath the variable that we declared for background, let’s declare one for player. I’ll include the previous line, so you can see where you should add it.

// This is our background image.
var background;

// This is our player.
var player;

Now let’s head into the handlePreload function, and get our player image ready to use.

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

    // Preload our "sky".
    this.load.image( 'starfield', 'starfield.png' );

    // Preload our player.
    this.load.image( 'ship', 'player.png' );
}

Finally, we’ll assign a value to our player variable, and make use of two new concepts in Phaser: origin and boundary collision.

First the code, and then the explanation!

// HOOK, PART 2: The create hook.
function handleCreate() {
    // Set starfield's value to be a tile sprite, and make sure it's scaled properly.
    // ... previous code ...
  
    // Add the player as a sprite to the game physics!
    player = this.physics.add.sprite( 400, 500, 'ship' );
    player.setOrigin( 0.5, 0 );
    player.setCollideWorldBounds( true );
}

Wait… why are we adding the player to physics instead of the scene itself? Good question. The simple answer is that our player has to respond to collisions.

What do I mean by that? It wouldn’t be much of a game if the player was invincible against the invaders. There has to be not only a bit of risk — if the player is hit too many times, it’s game over — but some skill as well, as the player will need to avoid the alien invaders’ bullets while also firing back. In order for one of the alien bullets to “hit” the player, Phaser must know that the player is capable of being hit.

In game development, this concept has a simple term: collision, which is when a game object — like our player — is being overlapped or has met the boundaries of another game object — like an alien bullet.

In Phaser, we accomplish that by adding our player variable to the scene’s physics collection. A scene’s physics is just a special group of variables that Phaser devotes a little more attention to.

You’ll also notice that instead of using the tileSprite like we did for starfield, we’re using just plain ol’ sprite for player. That’s because we don’t want to tile the image of the player. The image file itself is also not setup to be a tileable sprite. For both of these reasons, we simply use sprite.

When you compare tileSprite to sprite, you can see that we’re only sending in three arguments for sprite instead of the five for tileSprite. It takes more parameters because it needs more information about how to tile the image, where as the sprite simply needs to know the x- and y-coordinates of the center point of the rendered variable on the scene. We know that the canvas is only 800 pixels wide, according to our Phaser configuration, so we know that 400 is right in the middle of the screen horizontally. I chose 500 as the number of pixels from the top just because it seemed like a good spot. And finally, we provide the name of the preloaded image to use.

I made a point to highlight ”center point” above because the next line sets the sprite’s origin. Imagine that you’re looking down on an open cardboard box. You want to put a single item into it. Where in the box do you put it. Against the left edge of the box? The right edge? Or the middle?

Here’s a really crappy sketch I did to help you visualize:

Visualizing origins
Seriously, it's not a great drawing.

When we set the origin, we set the sprite’s horizontal and vertical alignment within an invisible space that Phaser has allotted for it. We moved its vertical origin to the “front,” so we can register a bullet collision just a fraction of an instant earlier.

Then we see setCollideWorldBounds. This is set to true because otherwise the player could press the left or right arrow keys, and keep going right off the scene. We want the edge of the scene to prevent them from going any farther, and setCollideWorldBounds is how we do it.

Here’s the few lines of code that go along with that long explanation!

Adding the player
It took more time to read the explanation than it did to write the code!

Go ahead and save your invaders.js file, and as soon as you do your browser will automatically refresh to show you the latest changes.

But we can’t stop here. This is bat country. Let us continue!

Controlling Our Hero

Let’s declare another variable called cursors just beneath player.

// This is our player.
var player;

// These are the keyboard buttons we're keeping track of.
var cursors;

And inside our handleCreate function, just beneath our code to add the player to the scene, we’ll assign a value to cursors.

player.setCollideWorldBounds( true );

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

This line is basically telling Phaser, “I want to use the arrow keys on the keyboard for this scene. Give me a reference to those keys, and I’ll store them in my cursors variable.”

We’re on a roll!

It’s been a while since we’ve given our handleUpdate function any love, so let’s show it some now. Inside handleUpdate, just beneath the starfield scrolling:

// Scroll our starfield background.
starfield.tilePositionY += 2;

// Is the player pressing the left arrow?
if ( cursors.left.isDown ) {
    player.setVelocityX( -200 );
}

// Is the player pressing the right arrow?
else if ( cursors.right.isDown ) {
    player.setVelocityX( 200 );
}

// Otherwise, we need to slow them down.
else {
    player.setVelocityX( 0 );
}

What we’re looking for here is whether the left or right arrow keys are being held down. Remember, the Phaser train runs through the handleUpdate function 60 times every second, so we don’t just want to look for a press of the keyboard. We actually need to pay close enough attention to determine if either of those keys are being pressed all the way down right now.

If they are, we use the scene’s physics to push the player in one direction or the other. If they let go of either of those keys, we stop moving completely. We use a negative number for the left arrow because we want to subtract from the player’s x-coordinate, and a positive number for the right arrow to add to it.

Let’s see it all together now.

Keyboard controls added.
Keyboard arrow key controls added.

Go ahead and take a minute. Play around in the browser. Use the arrow keys to see that not only can you move the player, but you can’t leave the scene by going too far. Admire your handiwork!

As cool as that is, it gets boring after a minute, am I right? Let’s create some alien invaders.

Creating Our “Bad Guys”

Once again, define a new variable to hold our bad guys. This one is named aliens, and we’ll add it just below the cursors one we created a minute ago.

// These are the keyboard buttons we're keeping track of.
var cursors;

// This is our collection of alien invaders.
var aliens;

We need to preload an image to use for our invaders, but this time we’re going to use a spritesheet. I actually discussed this a bit in Let’s Make a Game: Invaders (Part 2), and as a refresher this is an image that contains many frames that — when Phaser flips through them like a flipbook animation — gives the impression of movement.

The spritesheet loader takes three parameters: the unique name that we give every loaded asset, the file path to the image asset, and an object containing each “frame” of the animation’s width and height.

Go ahead and put this into our handlePreload function:

// Preload our player.
this.load.image( 'ship', 'player.png' );

// Preload the enemy image.
this.load.spritesheet( 'invader', 'invader50x60x10.png', {
    frameWidth: 50,
    frameHeight: 60
});

Notice that we’re using invader50x60x10.png and *not invader.png.* Have a look:

Variable declaration and spritesheet
Make sure you use the correct image!

Let’s head into our handleCreate function, and actually assign a value to our aliens variable.

Since we’re going to be creating multiple bad guys, aliens is going to be a Phaser group. We’re going to move the actual creation of each alien into a separate function, so we only need two lines of code in handleCreate. Add these just below our player code, but above cursors.

player.setCollideWorldBounds( true );

// Create a group to hold our invaders.
aliens = this.physics.add.group();
createAliens();

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

If you save right now — and you should save “early and often” as the phrase goes — your game will be broken. That’s because we haven’t written the createAliens function yet, but we’re trying to use it anyway.

Let’s fix that.

Below the handleUpdate function, add the following:

// This will create our collection of aliens.
function createAliens() {

    // We want 3 rows of 10 aliens each.
    for ( var y = 0; y < 3; y++ ) {
        for ( var x = 0; x < 10; x++ ) {
            var alien = aliens.create( x * 75, y * 90, 'invader' );
            alien.setOrigin( 0.5, 0.5 );
            alien.lastFired = 0;
            // TODO: Hover animation goes here.
        }
    }

    // Center our collection of aliens.
    Phaser.Actions.IncX( aliens.getChildren(), 60 );

    // Bring them further into the scene vertically.
    Phaser.Actions.IncY( aliens.getChildren(), 75 );
}

Alright, lots to unpack here. It’s actually fairly simple once you get the hang of reading for loops.

We want 30 aliens in 3 rows of 10, so we create our first for loop to count from 0 to 3 (our rows). Inside that loop, we create our second for loop to count from 0 to 10 (for each alien in that row). Then we create a new variable that will only be accessible inside this for loop, or inside the aliens group. Once we leave the inside (second) for loop, the alien variable won’t be defined. We refer to this as object or variable scope, and it’s absolutely a topic that we’ll see many times in our projects.

Our alien variable is assigned using our aliens group’s create method, which takes 3 parameters, just like the player variable did. We’re doing some fancy math here because we don’t want our 30 aliens to overlap each other. We need to offset them.

So as we do the first row, all of that row’s aliens will appear next to each other with 75 pixels of space between them. Then when the next row begins, we move them down 90 pixels. This will give us that classic grid-like appearance from the old retro shooter games. And don’t forget we need to use the unique name that we gave to our preloaded spritesheet.

We are — of course — setting each alien’s origin to be smack in the middle of its invisible Phaser box.

Then there’s something that’s not a part of Phaser. We’re setting the alien’s lastFired property to 0. This is something that we’re keeping track of, and I’ll discuss it more fully in the next post when we create the combat mechanics. For now, we’ll just leave it there.

Notice that I added a comment inside the inner loop that says, “Hover animation goes here.” Keep an eye on that because we’re going to be coming back to it.

After both of our loops are finished, we need to horizontally and vertically center the aliens. Thankfully, Phaser lets us do this with the entire group. Phaser.Actions.IncX is a handy helper that “increments the x-coordinate,” taking two parameters. The first being the Phaser entity we wish to move, and the second being how much we want to move it horizontally. Since group is a Phaser entity, we can use it here, and we specifically want to move everything inside the group. So we use a handy group function called getChildren to say, “Move all of these things 60 pixels farther right.”

Phaser.Actions.IncY does the same exact thing, except vertically.

Let’s see where we’re at now.

30 aliens created
Lots of pew-pew fodder here.

Animating Our Bad Guys

We’re in the home stretch for this post. Take a minute, stand up, stretch a little. When you’re ready, let’s continue.

Just inside of handleCreatebefore anything else — add the following code:

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

    // Setup our aliens' "hover" animation.
    this.anims.create({
        key: 'hover',
        frames: this.anims.generateFrameNumbers( 'invader', {
            start: 0,
            end: 9
        }),
        frameRate: 10,
        repeat: -1
    });
  
    // ... rest of our create code ...
}

We’re adding to the scene’s animations, and anims.create takes a configuration object as its parameter. Let’s break that object open a little.

The first property of that object is called key, and just like the unique names that we give to our preloaded assets, the animation needs one as well for the same reason (so we can refer to it by name when we need to use it).

The next property is called frames, and we’re actually going to populate it by using a Phaser animation helper function called generateFrameNumbers. Rather than telling Phaser about each and every frame inside that image sprite, this helper function will take a simple configuration — the unique preloaded image name, start frame, and end frame — and do some magic for us. Remember, in JavaScript collections begin their counting at 0, so when describing the start and end frame numbers, we use 0 for the first frame and 9 for the last frame.

Then comes a property called frameRate, which means “how many frames-per-second” should Phaser use when rendering our animation. We have 10 frames, and we want our animation to last 1 second. So we use 10 for our frameRate.

Finally, the property called repeat. This is exactly what it sounds like. How many times should Phaser play this animation once we start using it. We can define 0 to play it only once, or any other number. But we’re using -1, which is special, and it means, “Play it forever.”

Alien hover animation
Cool. An animation. But it's not useful yet.

We’ve defined the animation, but it’s not actually being used by anything yet. We have to attach it to something, and that’s super easy.

Go back down to our createAliens function. Find the line // TODO: Hover animation goes here., and replace the whole line with this:

alien.play( 'hover' );

Super easy, right? Here’s what that looks like in VSCode.

Alien hover animation attached
They're going to hover so good.

Make sure you save your invaders.js file!

Have a look at your browser! Pretty cool right?! It’ll be even cooler after the next post, when we implement combat mechanics. The aliens will start firing at the player, and the player will be able to fire right back!

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