After adding the health meter earlier this week, the playable sandbox level for my platformer built with
Phaser was looking much more complete. There are just a few more things I need to add before I switch my focus to level design and make a legitimate game out of it. One of those things is more weapons.
My plan for the game is to make it similar to a classic 8-bit Mega Man game. In those, after defeating one of the bosses, Mega Man could use the weapon from that boss. To make that happen in my game, I'd have to expand the weapons capability. I needed to add the ability to switch to different weapons that each had different qualities.
To begin, I defined an array of weapon objects. Each object would have its own name, velocity attributes, and power to damage enemies. For each of these weapon types, I will create a
Phaser Group to manage them. All the weapon groups will be added as children of an overall weapons group. Look at this code from my create function:
function create() {
// ... other stuff ...
weaponsGroup = game.add.group();
weapons = [];
weapons.push({
name: 'pencil',
lifespan: 1500,
velocity: {
x: 500,
y: -400
},
spin: 50,
power: 10
});
weapons.push({
name: 'flask',
lifespan: 1500,
velocity: {
x: 600,
y: -600
},
spin: 50,
power: 20
});
weapons.push({
name: 'hammer',
lifespan: 1500,
velocity: {
x: 600,
y: -400
},
spin: 50,
power: 30
});
weapons.push({
name: 'brace',
lifespan: 1500,
velocity: {
x: 800,
y: -200
},
spin: 80,
power: 40
});
for (var w = 0; w < weapons.length; w++) {
var wg = game.add.group();
wg.createMultiple(3, weapons[w].name, 0, false);
weapons[w].sprites = wg;
weaponsGroup.add(wg);
}
// ... other stuff ...
}
The overall group,
weaponsGroup, is created first. Then I set up an array of 4 weapon types. The name is important because I have it set up to match the sprite asset key for that weapon. I then loop over the
weapons array and create a group just for a set of those weapons. I set that group as the
sprites field on the weapon object - I'll need to reference it later. Also, those groups are added as children of the
weaponsGroup.
At this point I have a group of weapons groups. Any of these can be use to strike an enemy, so they must be checked for enemy collisions. A slight change in my
update function takes care of that. The
overlap function can take an array of groups in addition to a single group, and the children field of a Group object is the array of child groups!
function update() {
// ... other collision checks ...
game.physics.arcade.overlap(enemies, weaponsGroup.children, hurtEnemy, null, this);
// ... other stuff ...
}
Okay, so how do I have the frog throw a particular type of weapon? First, I added a variable to the game called
weaponIndex. It is set to the index of whatever weapon is currently active. So far, I have 4 weapons which means that the index can be 0, 1, 2, or 3. Then in my
throwSomething function, I get the weapon object out of the
weapons array using the index and use its properties to perform the toss.
function throwSomething() {
// has it been long enough? can we throw something yet?
if (game.time.now > nextFire) {
nextFire = game.time.now + FIRE_RATE;
// see if a weapon is available from the group
var weapon = weapons[weaponIndex].sprites.getFirstExists(false);
if (weapon) {
weapon.exists = true;
weapon.anchor.setTo(0.5, 0.5);
weapon.reset(frog.body.position.x+20, frog.body.position.y-20);
game.physics.arcade.enable(weapon);
weapon.lifespan = weapons[weaponIndex].lifespan;
weapon.body.angularVelocity = weapons[weaponIndex].spin;
weapon.body.velocity.y = weapons[weaponIndex].velocity.y;
weapon.power = weapons[weaponIndex].power;
if (frog.scale.x == 1) {
weapon.body.velocity.x = weapons[weaponIndex].velocity.x;
} else{
weapon.body.velocity.x = -weapons[weaponIndex].velocity.x;
}
}
}
}
This code is very similar to what I had before, when the only weapon was the pencil. The differences are that the sprite, lifespan, velocity, and power are pulled from the
weapon object in the
weapons array instead of always being hard-coded to the same values.
One final change was to the function that handles hurting the enemies when there is a collision between a weapon and an enemy. Previously, I used a fixed damage value for the enemy. Now, the damage to the enemy is based on the
power field of the particular weapon that is hitting it. For example, it takes 4 hits with a pencil to kill a toad but only 1 hit from a curly brace.
function hurtEnemy(e, w) {
if (!e.immune) {
e.immune = true;
e.alpha = 0.5;
// damage to enemy = power of weapon
e.damage(w.power);
w.exists = false;
game.time.events.add(200, function() {
e.immune = false;
e.alpha = 1;
}, this);
}
}
The
hurtEnemy function is what gets called when overlap is detected between a sprite in the enemies group and a sprite in one of the weapons groups. The first parameter
e will be the enemy object and the second parameter
w will be the weapon that hit it. Since my
throwSomething function set the
power field on the weapon sprite to match the
power field on the weapon definition, I can read that property here to control the amount of damage to the enemy.
Want to give it a try for yourself? Play the game here:
http://amphibian.com/eight-bit/. Just hit 1 through 4 on the keyboard to switch between the different weapons. If you'd like to see the complete source code,
it's on GitHub.
As for comics today, this is
the first of a 5-part Christmas story. It roughly follows the pattern of Dicken's
A Christmas Carol, but...well, you'll see. They're a bit longer than the normal ones, but don't take too much longer to read. I hope you enjoy them!