Talking about DROP'd game, Game development, HTML5, Javascript and Phaser.
What a cute game is DROP’d by GalacticThumb!
You just have to tap to drop your character on the platform immediately below, it’s fun but not that easy.
As usual, being an hyper casual 2D game, it’s quite easy to build a HTML5 prototype powered by Phaser and Arcade physics.
In less than 150 lines of code, I managed to pack:
* Endless moving physics platforms with object pooling. Actually, the platform are just 10
* Platform velocity with some kind of easing using trigonometry.
* Collision management to check the right platform to land on.
* Platforms are slippery and the hero will fall down if landing too close to platform edges.
* Camera movement following the action. Normally, in endless runner games the main character does not move, and it’s the entire environment which moves towards it. In this case, I found more convenient to move the main character and have a camera following it.
Have a look at the prototype:
Tap to destroy green platform, and try to land on yellow platform. If you miss it, it’s game over.
The source code is uncommented at the moment, but I am going to add comments when I’ll introduce some new features:
let game;
let gameOptions = {
firstPlatformPosition: 2 / 10,
gameGravity: 1700,
platformHorizontalSpeedRange: [250, 400],
platformLengthRange: [120, 300],
platformVerticalDistanceRange: [150, 250]
}
window.onload = function() {
let gameConfig = {
type: Phaser.AUTO,
backgroundColor:0x0c1445,
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
parent: "thegame",
width: 750,
height: 1334
},
physics: {
default: "arcade",
arcade: {
gravity: {
y: gameOptions.gameGravity
}
}
},
scene: playGame
}
game = new Phaser.Game(gameConfig);
window.focus();
}
class playGame extends Phaser.Scene {
constructor() {
super("PlayGame");
}
preload() {
this.load.image("hero", "hero.png");
this.load.image("platform", "platform.png");
}
create() {
this.platformGroup = this.physics.add.group();
for (let i = 0; i < 10; i ++) {
this.addPlatform(i == 0);
}
this.hero = this.physics.add.sprite(game.config.width / 2, 0, "hero");
this.hero.setFrictionX(1);
this.canDestroy = false;
this.cameras.main.startFollow(this.hero, true, 0, 0.5, 0, - (game.config.height / 2 - game.config.height * gameOptions.firstPlatformPosition));
this.input.on("pointerdown", this.destroyPlatform, this);
}
addPlatform(isFirstPlatform) {
let platform = this.platformGroup.create(game.config.width / 2,isFirstPlatform ? game.config.width * gameOptions.firstPlatformPosition : 0, "platform");
platform.isHeroOnIt = false;
platform.setImmovable(true);
platform.body.setAllowGravity(false);
platform.setFrictionX(1);
if(!isFirstPlatform) {
this.positionPlatform(platform);
}
else {
platform.setTint(0xffff00)
}
platform.assignedVelocityX = isFirstPlatform ? 0 : this.randomValue(gameOptions.platformHorizontalSpeedRange) * Phaser.Math.RND.sign();
}
paintSafePlatforms() {
let floorPlatform = this.getHighestPlatform(0);
floorPlatform.setTint(0x00ff00);
let targetPlatform = this.getHighestPlatform(floorPlatform.y);
targetPlatform.setTint(0xffff00);
}
handleCollision(hero, platform) {
if (!platform.isHeroOnIt) {
if (!platform.isTinted) {
this.scene.start("PlayGame")
}
if (hero.x < platform.getBounds().left) {
hero.setVelocityY(-200);
hero.setVelocityX(-200);
hero.angle = -45;
}
if (hero.x > platform.getBounds().right) {
hero.setVelocityY(-200);
hero.setVelocityX(200);
hero.angle = 45;
}
platform.isHeroOnIt = true;
this.paintSafePlatforms();
this.canDestroy = true;
}
}
randomValue(a) {
return Phaser.Math.Between(a[0], a[1]);
}
destroyPlatform() {
if (this.canDestroy) {
this.canDestroy = false;
let closestPlatform = this.physics.closest(this.hero).gameObject;
let furthestPlatform = this.physics.furthest(this.hero);
closestPlatform.isHeroOnIt = false;
closestPlatform.y = furthestPlatform.gameObject.y + this.randomValue(gameOptions.platformVerticalDistanceRange);
closestPlatform.assignedVelocityX = this.randomValue(gameOptions.platformHorizontalSpeedRange) * Phaser.Math.RND.sign();
closestPlatform.x = game.config.width / 2;
closestPlatform.displayWidth = this.randomValue(gameOptions.platformLengthRange);
closestPlatform.clearTint()
}
}
getLowestPlatform() {
let lowestPlatform = null;
this.platformGroup.getChildren().forEach(function(platform) {
lowestPlatform = Math.max(lowestPlatform, platform.y);
});
return lowestPlatform;
}
getHighestPlatform(maxHeight) {
let highestPlatform = null;
this.platformGroup.getChildren().forEach(function(platform) {
if ((platform.y > maxHeight) && (!highestPlatform || platform.y < highestPlatform.y)) {
highestPlatform = platform;
}
});
return highestPlatform;
}
positionPlatform(platform) {
platform.y = this.getLowestPlatform() + this.randomValue(gameOptions.platformVerticalDistanceRange);
platform.x = game.config.width / 2;
platform.displayWidth = this.randomValue(gameOptions.platformLengthRange);
}
update() {
if (this.hero.angle == 0) {
this.physics.world.collide(this.hero, this.platformGroup, this.handleCollision, null, this);
}
this.platformGroup.getChildren().forEach(function(platform) {
if (platform.y + game.config.height < this.hero.y) {
this.scene.start("PlayGame")
}
let distance = Math.max(0.2, 1 - ((Math.abs(game.config.width / 2 - platform.x) / (game.config.width / 2)))) * Math.PI / 2;
platform.setVelocityX(platform.assignedVelocityX * distance);
if ((platform.body.velocity.x < 0 && platform.getBounds().left < this.hero.displayWidth / 2) || (platform.body.velocity.x > 0 && platform.getBounds().right > game.config.width - this.hero.displayWidth / 2)) {
platform.assignedVelocityX *= -1;
}
}, this);
}
}
And this is another great game turned into a quick prototype thanks to the power of Phaser. 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.