Getting Started in Phaser 3 (ES6) - Create a Boomdots game part 2
Welcome to the second part of getting started in phaser 3. We are trying to create a clone of the famous Boomdots game in phaser 3 in ES6.
In the previous part, we completed the development setup and added a scrolling bg. In this post, we are going to add the rocket and the alien, set the collisions and add some particle effects to make it look better.
Add rocket
Add the rocket.png into the assets folder.

Load the image in the preloader.
preload () {
...
this.load.image('rocket', 'assets/rocket.png')
}
In game.js create the rocket game object like this.
create () {
...
this.rocket = this.add.sprite(0, 160, 'rocket')
}
Add alien
Let us add the alien into the scene similar to how we added the rocket.

preloader.js
preload () {
...
this.load.image('rocket', 'assets/alien.png')
}
game.js
create () {
...
this.alien = this.add.sprite(0, -150, 'alien')
}
Remember, our game center position is 0,0, so we need a negative value for the alien to display above the mid position.
If we open up our browser, we can see the alien in the scene.

Creating and animating the alien
Now, change the alien starting position from -150 to -300. And call a resetAlien method which we are going to create.
this.alien = this.add.sprite(0, -300, 'alien') this.resetAlien()
The resetAlien method,
resetAlien () {
this.canUpdateAlien = true
this.alien.x = 0
this.alien.y = -300
}
We can call this resetAlien method whenever the user hits it. Ok, we will go through that later. To animate the alien, create a method called moveAlien with arguments time and delta.
constructor () {
...
this.alienTargetY = 0
this.canUpdateAlien = false
}
...
update (time, delta) {
this.scrollingBg.tilePositionY -= 1
if (this.canUpdateAlien) {
this.moveAlien(time, delta)
}
}
moveAlien (time, delta) {
// Moves the alien down to this.alienTargetY position
this.alien.y += (this.alienTargetY - this.alien.y) * 0.3
// Moves the horizontal position back and forth
// Multiplying the time to reduce the movement speed
// 80 is the maximum horizontal amount to move
this.alien.x = Math.sin(time * 0.005) * 80
}
Let's check how it appears.

Move rocket
In the create method, listen for the pointerdown event and move the rocket upwards and collide it with the alien.
constructor () {
super({
key: 'game',
physics: {
default: 'arcade',
arcade: {
debug: true,
// gravity: 0 //We don't need gravity for this game
}
}
})
...
}
create () {
...
//Enable physics on rocket and alien sprites
this.physics.world.enable([this.rocket, this.alien])
//Reset alien so it will spawn from the top and start moving
this.resetAlien()
//Listen for pointerdown event and launch the rocket
this.input.on('pointerdown', this.launchRocket, this)
}
update (time, delta) {
...
//Listen for overlapping between rocket and alien and call rocketCollidedWithAlien when an overlap occurs
if (this.canUpdateAlien) {
this.moveAlien(time, delta)
this.physics.add.overlap(this.rocket, this.alien, this.rocketCollidedWithAlien, null, this)
}
}
...
launchRocket () {
//Launching means decrease the y velocity
this.rocket.body.setVelocity(0, -3000)
}
rocketCollidedWithAlien (rocket, alien) {
//Stop updating alien movement
this.canUpdateAlien = false
//Stop moving the rocket
this.rocket.body.setVelocity(0)
//Move the alien out of our screen for now
this.alien.y = -300
}
...
shutdown () {
this.input.off('pointerdown', this.launchRocket, this)
}
Add collision particles
First, we need to load the particle sprite in preloader.js
this.load.image('particle', 'assets/squareparticle.png')
Create the particle manager and an emitter. We are also going to add a camera shake.
constructor () {
...
this.particles = null
this.emitter = null
}
...
create () {
...
this.particles = this.add.particles('particle')
this.emitter = this.particles.createEmitter({
angle: { min: 0, max: 360 },
speed: { min: 50, max: 200 },
quantity: { min: 40, max: 50 },
lifespan: { min: 200, max: 500},
alpha: { start: 1, end: 0 },
scale: { min: 0.5, max: 0.5 },
rotate: { start: 0, end: 360 },
gravityY: 800,
on: false
})
}
...
rocketCollidedWithAlien (rocket, alien) {
if (!this.canUpdateAlien) { //Overlap runs multiple frames, we only want it to run once
return
}
this.canUpdateAlien = false
this.rocket.body.setVelocity(0)
this.particles.emitParticleAt(this.alien.x, this.alien.y)
this.alien.y = -300
this.cameras.main.shake(100, 0.01, 0.01) //Duration, intensity, force
}
After hitting the alien, we need to reset the rocket back to the original position. We also need to scroll the bg a little.
constructor () {
...
this.isRocketResetting = false
}
update (time, delta) {
if (this.canUpdateAlien) {
this.moveAlien(time, delta)
this.physics.add.overlap(this.rocket, this.alien, this.rocketCollidedWithAlien, null, this)
}
if (this.isRocketResetting) {
//Scroll the bg
this.scrollingBg.tilePositionY -= delta
//Move rocket down
this.rocket.y += delta
if (this.rocket.y >= 160) {
this.rocket.y = 160
this.isRocketResetting = false
//After movement reset alien so the next alien comes
this.resetAlien()
}
}
}
resetAlien () {
this.canUpdateAlien = true
this.alien.x = 0
this.alien.y = -300
this.alienTargetY = phaser.Math.Between(-200, 0)
}
rocketCollidedWithAlien (rocket, alien) {
if (!this.canUpdateAlien) {
return
}
this.canUpdateAlien = false
this.rocket.body.setVelocity(0)
this.particles.emitParticleAt(this.alien.x, this.alien.y)
this.alien.y = -300
this.cameras.main.shake(100, 0.01, 0.01)
this.time.delayedCall(200, this.resetRocket, [], this)
}
resetRocket () {
this.isRocketResetting = true
}
Let us place some triangle spikes on top and the bottom end of the screen.
Phaser 3 has various built-in position align helpers. We can use the camera rectangle and align the spikes to the top and bottom of that rectangle. But there is a problem in that, we are using the camera zoom. So for the correct dimensions, we need to divide the camera dimensions by the camera zoom amount. Like this.
constructor () {
...
this.topSpikes = null
this.bottomSpikes = null
this.cameraRect = null
}
...
create () {
this.topSpikes = this.add.sprite(0, 0, 'spike')
this.topSpikes.setOrigin(0.5, 0)
this.bottomSpikes = this.add.sprite(0, 0, 'spike')
this.bottomSpikes.setOrigin(0, 1)
this.bottomSpikes.flipY = true
this.cameraRect = this.add.zone(0, 0, 0, 0)
...
this.physics.world.enable([this.rocket, this.alien, this.topSpikes])
this.topSpikes.body.immovable = true
}
...
update (time, delta) {
...
if (!this.isGameOver) {
this.physics.add.overlap(this.rocket, this.topSpikes, this.rocketCollidedWithSpike, null, this)
}
...
}
resize () {
...
this.cameraRect.x = cam.x
this.cameraRect.y = cam.y
this.cameraRect.width = cam.width/cam.zoom
this.cameraRect.height = cam.height/cam.zoom
phaser.Display.Align.In.TopCenter(this.topSpikes, this.cameraRect)
phaser.Display.Align.In.BottomCenter(this.bottomSpikes, this.cameraRect)
}
...
rocketCollidedWithSpike (rocket, spike) {
if (this.isGameOver) {
return
}
this.canUpdateAlien = false
this.isGameOver = true
this.rocket.body.setVelocity(0)
this.particles.emitParticleAt(this.rocket.x, this.rocket.y-this.rocket.height)
this.cameras.main.shake(100, 0.01, 0.01)
this.alien.destroy()
this.rocket.destroy()
}
...
That's it. The core game is complete. There are only small things remaining like scoring and gameover scenes that you can manage yourself.
The complete code for preloader.js
import { Scene } from 'phaser'
export class Preloader extends Scene{
constructor(){
super({
key: 'preloader'
})
}
preload () {
this.load.image('bg-static', 'assets/square.png')
this.load.image('bg-overlay', 'assets/bg.png')
this.load.image('rocket', 'assets/rocket.png')
this.load.image('alien', 'assets/alien.png')
this.load.image('particle', 'assets/squareparticle.png')
this.load.image('spike', 'assets/spikes.png')
}
create () {
this.scene.start('game')
}
}
And for game.js
import phaser, { Scene } from 'phaser'
export class Game extends Scene {
constructor () {
super({
key: 'game',
physics: {
default: 'arcade',
arcade: {
debug: true
}
}
})
this.staticBg = null
this.scrollingBg = null
this.rocket = null
this.scrollSpeed = 0
this.alien = null
this.alienTargetY = 0
this.canUpdateAlien = false
this.particles = null
this.emitter = null
this.isRocketResetting = false
this.topSpikes = null
this.bottomSpikes = null
this.cameraRect = null
this.isGameOver = false
}
create () {
this.staticBg = this.add.image(0, 0, 'bg-static')
this.staticBg.setTint(0x444444)
this.staticBg.setOrigin(0.5)
this.scrollingBg = this.add.tileSprite(0,0,396,529,'bg-overlay')
this.scrollingBg.setOrigin(0.5)
this.topSpikes = this.add.sprite(0, 0, 'spike')
this.topSpikes.setOrigin(0.5, 0)
this.bottomSpikes = this.add.sprite(0, 0, 'spike')
this.bottomSpikes.setOrigin(0, 1)
this.bottomSpikes.flipY = true
this.cameraRect = this.add.zone(0, 0, 0, 0)
this.sys.game.events.on('resize', this.resize, this)
this.resize()
this.events.once('shutdown', this.shutdown, this)
this.rocket = this.add.sprite(0, 160, 'rocket')
this.alien = this.add.sprite(0, -300, 'alien')
this.physics.world.enable([this.rocket, this.alien, this.topSpikes])
this.topSpikes.body.immovable = true
this.resetAlien()
this.particles = this.add.particles('particle')
this.emitter = this.particles.createEmitter({
angle: { min: 0, max: 360 },
speed: { min: 50, max: 200 },
quantity: { min: 40, max: 50 },
lifespan: { min: 200, max: 500},
alpha: { start: 1, end: 0 },
scale: { min: 0.5, max: 0.5 },
rotate: { start: 0, end: 360 },
gravityY: 800,
on: false
})
this.input.on('pointerdown', this.launchRocket, this)
}
resize () {
let cam = this.cameras.main
cam.setViewport(0,0,window.innerWidth, window.innerHeight)
cam.centerToBounds()
cam.zoom = Math.max(window.innerWidth/270, window.innerHeight/480)
// cam.zoom = Math.min(window.innerWidth/270, window.innerHeight/480)
this.cameraRect.x = cam.x
this.cameraRect.y = cam.y
this.cameraRect.width = cam.width/cam.zoom
this.cameraRect.height = cam.height/cam.zoom
phaser.Display.Align.In.TopCenter(this.topSpikes, this.cameraRect)
phaser.Display.Align.In.BottomCenter(this.bottomSpikes, this.cameraRect)
}
update (time, delta) {
if (this.canUpdateAlien) {
this.moveAlien(time, delta)
this.physics.add.overlap(this.rocket, this.alien, this.rocketCollidedWithAlien, null, this)
}
if (!this.isGameOver) {
this.physics.add.overlap(this.rocket, this.topSpikes, this.rocketCollidedWithSpike, null, this)
}
if (this.isRocketResetting) {
this.scrollingBg.tilePositionY -= delta
this.rocket.y += delta
if (this.rocket.y >= 160) {
this.rocket.y = 160
this.isRocketResetting = false
this.resetAlien()
}
}
}
resetAlien () {
this.canUpdateAlien = true
this.alien.x = 0
this.alien.y = -300
this.alienTargetY = phaser.Math.Between(-100, 0)
}
moveAlien (time, delta) {
this.alien.y += (this.alienTargetY - this.alien.y) * 0.3
this.alien.x = Math.sin(time * 0.005) * 80
}
launchRocket () {
this.rocket.body.setVelocity(0, -2000)
}
rocketCollidedWithAlien (rocket, alien) {
if (!this.canUpdateAlien) { //Overlap runs multiple frames, we only want it to run once
return
}
this.canUpdateAlien = false
this.rocket.body.setVelocity(0)
this.particles.emitParticleAt(this.alien.x, this.alien.y)
this.alien.y = -300
this.cameras.main.shake(100, 0.01, 0.01)
this.time.delayedCall(200, this.resetRocket, [], this)
}
rocketCollidedWithSpike (rocket, spike) {
if (this.isGameOver) {
return
}
this.canUpdateAlien = false
this.isGameOver = true
this.rocket.body.setVelocity(0)
this.particles.emitParticleAt(this.rocket.x, this.rocket.y-this.rocket.height)
this.cameras.main.shake(100, 0.01, 0.01)
this.alien.destroy()
this.rocket.destroy()
}
resetRocket () {
this.isRocketResetting = true
}
shutdown () {
this.input.off('pointerdown', this.launchRocket, this)
this.sys.game.events.off('resize', this.resize, this)
}
}
Thanks for reading.
If you want to get notified about new articles like these, please subscribe to our Newsletter.
[Total: 0 Average: 0/5]
