Talking about Color Jump game, Game development, HTML5, Javascript and Phaser.
As promised, here we go with the second step of Color Jump prototype.
In the first step we saw the basic prototype of the game, and now it’s time to add collectible coins, just like we did in the fourth part of “Don’t Touch the Spikes” prototype.
Look at the game:
Tap to jump. Hit the walls matching the color of the ball, or it’s game over. If you fall down, it’s game over. If you fly off the top, it’s game over. Try to collect the coins.
Exactly like in Don’t Touch the Spikes, coins introduce a new kind of Matter body called sensor. Sensors trigger collision events, but do not react with colliding body physically, so the player won’t bounce on the coin, and it’s exactly what we need.
And now you have the full commented source code, line by line:
// the game itself
let game;
// global game options
let gameOptions = {
// world gravity
gravity: 4,
// ball horizontal speed
ballSpeed: 4,
// jump force
jumpForce: 30,
// amount of bars each wall is divided in
bars: 4,
// array with the colors to pick from
barColors: [0x1abc9c, 0x2980b9, 0x9b59b6, 0xf1c40f, 0xc0392b, 0xecf0f1]
}
// constants used to pass "LEFT" and "RIGHT" as arguments rather than "0" and "1"
const LEFT = 0;
const RIGHT = 1;
// function to be executed when the windows has loaded
window.onload = function() {
// object containing configuration options
let gameConfig = {
// render type: let the game decide if CANVAS of WEBGL
type: Phaser.AUTO,
// width of the game, in pixels
width: 750,
// height of the game, in pixels
height: 1334,
// background color (black)
backgroundColor: 0x000000,
// scene to play
scene: playGame,
// physics settings
physics: {
// we are using Matter JS
default: "matter",
matter: {
// gravity settings
gravity: {
x: 0,
y: gameOptions.gravity
}
}
}
}
// game creation
game = new Phaser.Game(gameConfig);
// giving focus to the frame (if any) where the game is running in
window.focus();
// pure javascript to scale the canvas
resize();
window.addEventListener("resize", resize, false);
}
// playGame scene
class playGame extends Phaser.Scene{
constructor(){
super("PlayGame");
}
// preloading assets
preload(){
this.load.image("wall", "wall.png");
this.load.image("ball", "ball.png");
this.load.image("coin", "coin.png");
}
// method to be executed once, when the scene has been created
create(){
// arrays where to store left and right walls
this.leftWalls = [];
this.rightWalls = [];
// each wall is made by "gameOptions.bars" pieces, so we actually have "gameOptions.bars" walls each side
for(let i = 0; i < gameOptions.bars; i++){
// adding left and right walls
this.leftWalls[i] = this.addWall(i, LEFT);
this.rightWalls[i] = this.addWall(i, RIGHT);
}
// adding the ball
this.ball = this.matter.add.image(game.config.width / 4, game.config.height / 2, "ball");
// setting ball body as circular
this.ball.setCircle()
// adding the coin, no matter where, we'll set its position later
this.coin = this.matter.add.image(0, 0, "coin");
// setting coin body as circular
this.coin.setCircle();
// setting coin body as static (not affected by gravity or collisions)
this.coin.setStatic(true);
// setting coin body as sensor. Will fire collision events without actually collide
this.coin.body.isSensor = true;
// giving the coin a label called "coin"
this.coin.body.label = "coin"
// this method will randomly place the coin
this.placeCoin();
// setting ball velocity (horizontal, vertical)
this.ball.setVelocity(gameOptions.ballSpeed, 0);
// waiting for pointer down input to call "jump" method
this.input.on("pointerdown", this.jump, this);
// waiting for a "collisionstart" event. "e" is the event, "b1" and "b2" the bodies
this.matter.world.on("collisionstart", function (e, b1, b2) {
// checking b1 and b2 labels to be "leftwall"
if(b1.label == "leftwall" || b2.label == "leftwall"){
// handling collisions on the LEFT side
this.handleWallCollision(LEFT, b1, b2);
}
// checking b1 and b2 labels to be "rightwall"
if(b1.label == "rightwall" || b2.label == "rightwall"){
// handling collisions on the RIGHT side
this.handleWallCollision(RIGHT, b1, b2);
}
// checking b1 and b2 labels to be "coin"
if(b1.label == "coin" || b2.label == "coin"){
// calling the method to move the coin elsewhere
this.placeCoin();
}
}, this);
}
// method to add a wall, given its number (0 = top) and it side
addWall(wallNumber, side){
// getting "wall" preloaded image
let wallTexture = this.textures.get("wall");
// determining wall height according to game height and the number of bars
let wallHeight = game.config.height / gameOptions.bars;
// determining wall x position
let wallX = side * game.config.width + wallTexture.source[0].width / 2 - wallTexture.source[0].width * side;
// determining wall y position
let wallY = wallHeight * wallNumber + wallHeight / 2;
// adding the wall
let wall = this.matter.add.image(wallX, wallY, "wall");
// the wall is static
wall.setStatic(true);
// giving the wall the proper label
wall.body.label = (side == RIGHT) ? "rightwall" : "leftwall"
// setting wall height
wall.displayHeight = wallHeight;
// returning the wall object
return wall
}
// method to place the coin
placeCoin(){
// just placing the coin in a random position between 20% and 80% of the game size
this.coin.x = Phaser.Math.Between(game.config.width * 0.2, game.config.width * 0.8);
this.coin.y = Phaser.Math.Between(game.config.height * 0.2, game.config.height * 0.8);
}
// method to handle ball Vs wall collision
handleWallCollision(side, bodyA, bodyB){
// if the ball and the wall have different colors...
if(bodyA.color != bodyB.color){
// restart the game
this.scene.start("PlayGame");
}
// calling a method to paint the walls
this.paintWalls((side == LEFT) ? this.rightWalls : this.leftWalls);
// updating ball velocity
this.ball.setVelocity(gameOptions.ballSpeed, this.ball.body.velocity.y);
}
// method to paint the walls, in the argument the array of walls
paintWalls(walls){
// looping through all walls
walls.forEach(function(wall){
// picking a random color
let color = Phaser.Math.RND.pick(gameOptions.barColors);
// tinting the wall
wall.setTint(color);
// also assigning the wall body a custom "color" property
wall.body.color = color;
});
// picking a random wall
let randomWall = Phaser.Math.RND.pick(walls);
// painting the ball with the same color used by the random wall
this.ball.setTint(randomWall.body.color);
// also assigning the ball body a custom "color" property
this.ball.body.color = randomWall.body.color;
}
// method to jump
jump(){
// setting new ball velocity
this.ball.setVelocity((this.ball.body.velocity.x > 0) ? gameOptions.ballSpeed : -gameOptions.ballSpeed, -gameOptions.jumpForce);
}
// method to be called at each frame
update(){
// updating ball velocity
this.ball.setVelocity((this.ball.body.velocity.x > 0) ? gameOptions.ballSpeed : -gameOptions.ballSpeed, this.ball.body.velocity.y);
// if the ball flies off the screen...
if(this.ball.y < 0 || this.ball.y > game.config.height){
// restart the game
this.scene.start("PlayGame");
}
}
};
// pure javascript to resize the canvas and scale the game
function resize(){
let canvas = document.querySelector("canvas");
let windowWidth = window.innerWidth;
let windowHeight = window.innerHeight;
let windowRatio = windowWidth / windowHeight;
let gameRatio = game.config.width / game.config.height;
if(windowRatio < gameRatio){
canvas.style.width = windowWidth + "px";
canvas.style.height = (windowWidth / gameRatio) + "px";
}
else{
canvas.style.width = (windowHeight * gameRatio) + "px";
canvas.style.height = windowHeight + "px";
}
}
What about some particle effects next time? I will show you how to add eye-candy effects. Download the source code.
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.