Creating a Shoot-em-up Tutorial

By Richard Davey on 23rd February 2015   @photonstorm

Welcome!

Shmupjam is a game jam dedicated to creating shoot-em-up games. As it's now in full flow I figured it'd be great to see some Phaser entries for it. Therefore this weeks Coding Tips is dedicated to all things bullety and shooty.

Get the source

I'm only going to highlight the most important parts of the code here. So please always look at the source first. If you've questions about something I didn't cover then ask on the forum.

Run / Edit the code on jsbin or codepen

Clone the phaser-coding-tips git repo.

Infinite Ammo

The whole point of a shoot-em-up is of course shooting things. For this you need bullets. Lots of them. Creating new objects in JavaScript (or indeed any language) is expensive. So what we do is pre-build all our bullets up front. Then as they are fired, they move across the screen and once outside we free them up, putting them back into the pool, ready to be reset and fired again as needed. This way we don't generate any new objects at all during the game loop.

To manage this I'm using a single Bullet object. This is essentially a Sprite with a couple of extra features:

var Bullet = function (game, key) {

    Phaser.Sprite.call(this, game, 0, 0, key);

    this.texture.baseTexture.scaleMode = PIXI.scaleModes.NEAREST;

    this.anchor.set(0.5);

    this.checkWorldBounds = true;
    this.outOfBoundsKill = true;
    this.exists = false;

    this.tracking = false;
    this.scaleSpeed = 0;

};

The first line tells Pixi to use nearest neighbour scaling. This means when the bullet is scaled from its default size it won't be automatically 'smoothed' as will retain its pixel crispness.

The checkWorldBounds and outOfBoundsKill lines pretty much do exactly what they say. They will check if the bullet is within the world bounds and if not kill it, freeing it up for use in the bullet pool again.

The tracking property tells the Bullet to rotate to face the direction it is moving in, as it moves. This is handled in its update method. Finally scaleSpeed is how fast the bullet should grow in size as it travels. Both of these settings are used by some of the weapons as you'll see shortly.

Weapons of Mass Destruction

The example has 10 different weapon types and 2 'combo' weapons - which are essentially combinations of the previous 10 types. However all weapons follow the exact same template. Here is the SingleBullet weapon:

Weapon.SingleBullet = function (game) {

    Phaser.Group.call(this, game, game.world, 'Single Bullet', false, true, Phaser.Physics.ARCADE);

    this.nextFire = 0;
    this.bulletSpeed = 600;
    this.fireRate = 100;

    for (var i = 0; i < 64; i++)
    {
        this.add(new Bullet(game, 'bullet5'), true);
    }

    return this;

};

As you can see it's a Group that has Arcade Physics enabled on it. There are 3 properties: nextFire is the time the player is allowed to shoot again. bulletSpeed is the speed the bullets this particular weapon fires travel at. And fireRate is the rate at which this weapon fires. The lower the number, the higher the firing rate.

It then creates a bunch of Bullet objects. In this weapon you can see it's creating 64 of them and they're all using 'bullet5' as the texture. If you look in the assets folder you'll see 11 different bullet textures you can use.

Finally the Weapon class has a fire method:

Weapon.SingleBullet.prototype.fire = function (source) {

    if (this.game.time.time < this.nextFire) { return; }

    var x = source.x + 10;
    var y = source.y + 10;

    this.getFirstExists(false).fire(x, y, 0, this.bulletSpeed, 0, 0);

    this.nextFire = this.game.time.time + this.fireRate;

};

The first thing it does is check if the player can fire or not, by comparing the game time with the nextFire property.

Then it gets the first bullet from the Group that has exists = false and fires it. The parameters to the fire method are:

Bullet.fire(x, y, angle, speed, gx, gy)

Where x and y are the coordinates the bullet is spawned at. angle is the angle to which it will be rotated. speed is the speed it travels and gx and gy are optional gravity values that allow you to make the bullets 'bend' as they travel.

All movement is calculated based on the angle. The Bullet.fire method uses an Arcade Physics helper function called velocityFromAngle which sets the bullet velocity based on a given angle and speed. To this end we can simply fire a bullet in front of the player by giving it an angle of zero, and that's exactly what the first weapon type does.

Single Bullet

weapon1

This weapon typ is as traditional as they come, but it gets the job done.

This fires a single bullet in front of the ship. You can adjust the speed and rate by tweaking the bulletSpeed and fireRate values (lines 82 and 83 of the source code). You can also adjust the bullet texture by changing bullet5 on line 87.

Front and Back

weapon2

Here we fire two bullets at once. One in front of the ship and one behind. This is done by simply giving the first bullet and angle of 0 and the second an angle of 180:

this.getFirstExists(false).fire(x, y, 0, this.bulletSpeed, 0, 0);
this.getFirstExists(false).fire(x, y, 180, this.bulletSpeed, 0, 0);

And now you can dispatch baddies that creep up on you from behind.

Three Way

weapon3

Taking the 'fire at an angle' one step further you can now shoot above and below your ship. As you may imagine this is done by specifying 3 angles:

this.getFirstExists(false).fire(x, y, 270, this.bulletSpeed, 0, 0);
this.getFirstExists(false).fire(x, y, 0, this.bulletSpeed, 0, 0);
this.getFirstExists(false).fire(x, y, 90, this.bulletSpeed, 0, 0);

270 degrees being above the ship, 0 being in front and 90 being down. Naturally we can expand on this even further ...

Eight Way

weapon4

Alrighty! Now we're cooking. By releasing 8 bullets every time, all spread out 45 degrees apart, we can cause some serious mayhem on-screen.

Scatter Shot

weapon5

It's not all just about angles though. With the Scatter Shot weapon type the bullets are released on slightly varying y axis values, giving a nice unconventional stream darting across the screen. This is done by applying a slight random value to the y value on which the bullet is launched from:

var y = (source.y + source.height / 2) + this.game.rnd.between(-10, 10);

this.getFirstExists(false).fire(x, y, 0, this.bulletSpeed, 0, 0);

Here it's -+ 10 pixels from the center of the ship.

Beam

weapon6

But what if we don't vary the y axis at all? But instead dramatically ramp up the rate of fire? Then we get a nice 'beam' style gun. The code is identical to the Single Bullet weapon type, just with a much faster rate and different texture:

this.bulletSpeed = 1000;
this.fireRate = 45;

Split Shot

weapon7

Remember how the Bullet class has optional gravity parameters to its fire method? By setting these we can create all kinds of bullets that appear to 'bend' as they are unleashed. This Split Shot being a good example.

It works by releasing 3 bullets per shot, one directly in front but the other two have their own local gravity properties set to -500 and 500:

this.getFirstExists(false).fire(x, y, 0, this.bulletSpeed, 0, -500);
this.getFirstExists(false).fire(x, y, 0, this.bulletSpeed, 0, 0);
this.getFirstExists(false).fire(x, y, 0, this.bulletSpeed, 0, 500);

The gravity is local to just the bullet and causes it to be dragged up (or down) the screen as it travels, creating the bending effect. Adjust the 500 to see the effect it has in-game.

Pattern

weapon8

The Pattern weapon uses a pre-defined local array (this.pattern) which contains gravity values for the bullets to use as they are fired. Rather than being random, or fixed to just one value, this allows the bullets to cycle through the pre-defined list causing them to 'wave' as fire.

The pattern is defined in the constructor:

this.pattern = Phaser.ArrayUtils.numberArrayStep(-800, 800, 200);
this.pattern = this.pattern.concat(Phaser.ArrayUtils.numberArrayStep(800, -800, -200));

This basically creates an array between -800 to 800 with a step rate of 200. And then does the same in reverse. What this gives us is an array of gravity values we can cycle through. A bullet is fired using the first array value, then we move to the next and so-on in a loop. Depending on how creative you get with the patterns you can make some incredible effects this way.

Rockets

weapon9

Our penultimate weapon type are some rockets. These use the exact same principals as the Three Way Weapon, however the rocket texture is set to track the direction it travels in. This is done by setting tracking = true on all of the Bullets in the Group:

this.setAll('tracking', true);

Correspondingly the Bullet.update method checks if this is set, and if so it adjusts its rotation based on its velocity. Simple, but looks great!

Scale Bullet

weapon10

Our last weapon type fires a single forward-facing shot, but this time the bullet scales in size as it moves across the screen. This is done in the same way as the Rockets, by setting a scaleSpeed value on every Bullet in the Group. The Bullet knows when this is set and adjusts its scale accordingly.

The end result looks great for huge walls of fire!

Combo Time

So how about combining several of the weapons we've covered into one? Here is Combo 1:

Weapon.Combo1 = function (game) {

    this.name = "Combo One";
    this.weapon1 = new Weapon.SingleBullet(game);
    this.weapon2 = new Weapon.Rockets(game);

};

combo1

As you can see it's a combination of the SingleBullet weapon and the Rockets. When the player presses fire it just hands this over to the individual weapon classes:

Weapon.Combo1.prototype.fire = function (source) {

    this.weapon1.fire(source);
    this.weapon2.fire(source);

};

Pretty sweet! How about making that 3 weapons?

combo2

Heck yes! Super Nashwan Power!

Honestly I think this is where shoot-em-ups really come into their own. By putting together great combinations of audacious weapons and spreading bullet hell all over the screen. Obviously you need to create equally challenging aliens to lay waste to.

Hopefully this tutorial has shown how to create a powerful and easily extended Bullet / Weapon system for your game, that with a little thought and experimentation, could be taken into all kinds of fun directions.

Brain Food

Here are some ways to enhance this example:

  • Turn it side-ways and create a vertically scrolling shooter instead.
  • Give the Bullets acceleration instead of Velocity then watch them increase in speed over time.
  • Give the Rockets way points to home in on (homing missiles ahoy)
  • Add a rotating orb that flies around your ship and can shoot on its own.

Comments

comments powered by Disqus