Player Bullets
Now that we have asteroids hurtling towards us, we need to be able to shoot them out of the sky! Every spaceship needs some cannons to fight with, so lets move on and add a weapon to the player’s space ship.
Making it so that the player can shoot bullets is almost the same task as creating asteroids that fly down the screen. Now is a chance to put the skills you learned from creating asteroids to the test!
Task Definition
Bullets in Space Avengers should abide by the following rules;
- You should fire a new bullet if the player is pressing the ‘Z’ key on the keyboard.
- If the user is holding down the ‘Z’ key, you shouldn’t allow them to shoot more than 1 bullet every 0.3 seconds.
- All bullets should constantly move up the screen.
- Bullets that have disappeared off the top of the screen should be destroyed.
The stages for this task are very similar to that of the asteroids task, so you should refer back to that if the details seem a little limited here.
Hints
- This task is actually pretty similar to the asteroids task, and the user input is similar to the ‘player movement’ task.
- The main difference is that you will want to spawn bullets whenever you press the Z key instead of using a timer.
- However, you will still need to add another time to prevent the user from firing too many bullets at once!
The following Phaser properties and functions will be usfeul for this task;
this.fireKey = game.input.keyboard.addKey(Phaser.Keyboard.Z);
Just like with the cursor keys, you first need to make an object to keep track of the Z key. Once you have done that, you can use that object you created in the update loop to test whether that key is pressed this frame.
Walkthrough
1. Check for the ‘Z’ Key
Checking for whether the user is pressing the ‘Z’ key is almost identical to how we check whether the user is pressing an arrow key down. Check back to the player movement task and see if you can figure this step out from there. The only difference is, instead of creating a cursorKeys object, you need to make a specific object to track the status of a single key using this.fireKey = game.input.keyboard.addKey(Phaser.Keyboard.Z);.
Here are some useful code snippets for completing this task;
// Create this variable to 'watch' if the Z key is being pressed
this.fireKey = game.input.keyboard.addKey(Phaser.Keyboard.Z);
// Use this variable in your update loop to check if the fire key is pressed
if ( this.fireKey.isDown ) { ... }For now, why not just print some text to the console to make sure that your code is working correctly?
mainGameState.create = function() {
this.fireKey = game.input.keyboard.addKey(Phaser.Keyboard.Z);
}
mainGameState.update = function() {
if ( this.fireKey.isDown ) {
console.log("FIRE KEY PRESSED");
}
}2. Spawning Bullets
You should base your answer for this stage on the second stage of the Asteroid Task. Here is a summary of the task steps;
- Add the player bullet sprite to your project.
- Create a ‘spawnPlayerBullet’ function in your mainGameState.
- Spawn a bullet at the player’s X position.
- Give the bullet an upwards velocity.
- Call ‘spawnBullet’ when you know the fireKey is pressed down.
mainGameState.preload = function() {
...
game.load.image("player-bullet", "assets/images/bullet-fire.png");
}
mainGameState.create = function() {
...
this.fireKey = game.input.keyboard.addKey(Phaser.Keyboard.Z);
}
mainGameState.update = function() {
if ( this.fireKey.isDown ) {
this.spawnPlayerBullet();
}
}
mainGameState.spawnPlayerBullet = function() {
var bullet = game.add.sprite(this.player.x, this.player.y, "player-bullet");
bullet.anchor.setTo(0.5, 0.5);
game.physics.arcade.enable(bullet);
bullet.body.velocity.setTo(0, -100);
}3. Destroy off-screen bullets
This step is just like steps 3 and 4 from the asteroids task and you should do the following;
- Create a group to hold the playerBullets using this.playerBullets = game.add.group();
- Add bullets to that group whenever you spawn a new one.
- At the end of your update function, iterate over the array of playerBullets by accessing this.playerBullets.children.
- For each bullet that is way off the top of the screen - destroy it by calling the this.playerBullets.children[i].destroy() function.
mainGameState.preload = function() {
...
game.load.image("player-bullet", "assets/images/bullet-fire.png");
}
mainGameState.create = function() {
...
this.playerBullets = game.add.group();
}
mainGameState.update = function() {
if ( this.fireKey.isDown ) {
spawnPlayerBullet();
}
for (var i = 0; i < this.playerBullets.children.length; i++) {
if ( this.playerBullets.children[i].y < -200 ) {
this.playerBullets.children[i].destroy();
}
}
}
mainGameState.spawnPlayerBullet = function() {
var bullet = game.add.sprite(this.playerShip.x, this.playerShip.y, "player-bullet");
bullet.anchor.setTo(0.5, 0.5);
game.physics.arcade.enable(bullet);
bullet.body.velocity.setTo(0, -200);
this.playerBullets.add(bullet);
}4. Restricting Bullet Count
If you have run the game since step 2, you will notice that when you press fire you shoot a near uncountable stream of bullets! This isn’t great because there isn’t going to be much challenge in the game if this is how it works.
We’re going to setup another timer like we did with the asteroids in order to restrict how many bullets the player can shoot. While similar, there are some subtle differences in this task to the asteroids;
- Create the variable this.fireTimer in your create method.
- In your update method, subtract the current frame time from the timer this.fireTimer -= game.timer.physicsElapsed.
- In your ‘shootBullet’ function, do not allow the user to shoot a bullet if the timer is above zero.
- If you do shoot a bullet, you will need to reset the fireTimer to prevent the player from shooting a stream of bullets.
mainGameState.preload = function() {
...
game.load.image("player-bullet", "assets/images/bullet-fire.png");
}
mainGameState.create = function() {
...
this.fireKey = game.input.keyboard.addKey(Phaser.Keyboard.Z);
this.fireTimer = 0.4;
}
mainGameState.update = function() {
if ( this.fireKey.isDown ) {
spawnPlayerBullet();
}
this.fireTimer -= game.time.physicsElapsed;
for (var i = 0; i < this.playerBullets.children.length; i++) {
if ( this.playerBullets.children[i].y < -200 ) {
this.playerBullets.children[i].destroy();
}
}
}
mainGameState.spawnPlayerBullet = function() {
if ( this.fireTimer < 0 ) {
this.fireTimer = 0.4;
var bullet = game.add.sprite(this.playerShip.x, this.playerShip.y, "player-bullet");
bullet.anchor.setTo(0.5, 0.5);
game.physics.arcade.enable(bullet);
bullet.body.velocity.setTo(0, -200);
this.playerBullets.add(bullet);
}
}Extensions
- The code in your update function continues to grow! Now would be a good time to break some of the code out into functions. Perhaps make an ‘mainGameState.updatePlayerBullets()’ function and move all of your bullet related update code there. Don’t forget to call that function from your main update function!
- It would also be a good idea to set timers for the minTimeBetweenPlayerShots and the playerBulletSpeed.
Sample Solution
If you have been following along with the sample solution, your project should look like this after completing this task.
var mainGameState = { }
mainGameState.preload = function() {
console.log("Pre-loading the game!");
game.load.image("space-bg", "assets/images/space-bg.jpg");
game.load.image("player-ship", "assets/images/player-ship.png");
game.load.image("asteroid-medium-01", "assets/images/asteroid-medium-01.png");
game.load.image("player-bullet", "assets/images/bullet-fire.png");
game.load.audio("game-music", "assets/music/maingame.mp3");
}
mainGameState.create = function() {
game.physics.startSystem(Phaser.Physics.ARCADE);
game.add.sprite(0, 0, "space-bg");
var shipX = game.width * 0.5;
var shipY = game.height * 0.8;
this.cursors = game.input.keyboard.createCursorKeys();
this.playerShip = game.add.sprite(shipX, shipY, 'player-ship');
this.playerShip.anchor.setTo(0.5, 0.5);
game.physics.arcade.enable(this.playerShip);
this.music = game.add.audio("game-music");
this.music.play();
this.music.volume = 0.5;
this.music.loop = true;
this.asteroidTimer = 2.0;
this.asteroids = game.add.group();
this.fireKey = game.input.keyboard.addKey(Phaser.Keyboard.Z);
this.playerBullets = game.add.group();
this.fireTimer = 0.4;
}
mainGameState.update = function() {
if ( this.cursors.left.isDown ) {
this.playerShip.body.velocity.x = -200;
} else if ( this.cursors.right.isDown ) {
this.playerShip.body.velocity.x = 200;
} else {
this.playerShip.body.velocity.x = 0;
}
this.asteroidTimer -= game.time.physicsElapsed;
if ( this.asteroidTimer <= 0.0 ) {
this.spawnAsteroid();
this.asteroidTimer = 2.0;
}
// Clean up any asteroids that have moved off the bottom of the screen
for( var i = 0; i < this.asteroids.children.length; i++ ) {
if ( this.asteroids.children[i].y > (game.height + 200) ) {
this.asteroids.children[i].destroy();
}
}
if ( this.fireKey.isDown ) {
this.spawnPlayerBullet();
}
this.fireTimer -= game.time.physicsElapsed;
for (var i = 0; i < this.playerBullets.children.length; i++) {
if ( this.playerBullets.children[i].y < -200 ) {
this.playerBullets.children[i].destroy();
}
}
}
mainGameState.spawnPlayerBullet = function() {
if ( this.fireTimer < 0 ) {
this.fireTimer = 0.4;
var bullet = game.add.sprite(this.playerShip.x, this.playerShip.y, "player-bullet");
bullet.anchor.setTo(0.5, 0.5);
game.physics.arcade.enable(bullet);
bullet.body.velocity.setTo(0, -200);
this.playerBullets.add(bullet);
}
}
mainGameState.spawnAsteroid = function() {
// Setup and create our asteroid
var x = game.rnd.integerInRange(0, game.width);
var asteroid = game.add.sprite(x, 0, "asteroid-medium-01");
asteroid.anchor.setTo(0.5, 0.5);
game.physics.arcade.enable(asteroid);
asteroid.body.velocity.setTo(0, 100);
// Add to the 'asteroids' group
this.asteroids.add(asteroid);
}