Talking about Mike Dangers game, Game development, HTML5, Javascript and Phaser.
Did you play Mike Dangers by Henrique Silva? It’s also available on Google Play. It’s an endless climber heavily influenced by old Commodore 64 glory Rick Dangerous. All you have to do is climb your way to the sky, ladder after ladder, avoiding spikes, traps and monsters while collecting diamonds and idols. Playing a bit with Phaser and ARCADE physics, I was able to replicate the main engine, as you can see from this prototype: Just tap or click to make the player jump. Try to climb the ladders. If you have a mobile device, you can play directly at this link. Despite the few lines of code – a bit more than 150 – I was able to add these features: * Fake endless runner feeling. You aren’t “climbing”, I just tween the game then reposition it. Remember endless runners aren’t actually “endless”, I wrote an entire book about it. * Object Pooling collection to use only a few sprites and only two tweens. If you aren’t familiar with object pooling, check this post. * ARCADE physics managemetn and collision handling * Eight different variables to play with to adjust gameplay This is why Phaser is so awesome. This is the source code of the first prototype, I have to add diamonds and enemies, then the game will be uploaded with line by line comments both here and on Learn Lazer. Anyway, the code is very simple and split into functions:
var game;
var gameOptions = {
gameWidth: 800,
gameHeight: 1300,
floorStart: 1 / 8 * 5,
floorGap: 250,
playerGravity: 4500,
playerSpeed: 450,
climbSpeed: 450,
playerJump: 900
}
window.onload = function() {
game = new Phaser.Game(gameOptions.gameWidth, gameOptions.gameHeight);
game.state.add("PreloadGame", preloadGame);
game.state.add("PlayGame", playGame);
game.state.start("PreloadGame");
}
var preloadGame = function(game){}
preloadGame.prototype = {
preload: function(){
game.stage.backgroundColor = 0xaaeaff;
game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
game.scale.pageAlignHorizontally = true;
game.scale.pageAlignVertically = true;
game.stage.disableVisibilityChange = true;
game.load.image("ground", "ground.png");
game.load.image("hero", "hero.png");
game.load.image("ladder", "ladder.png");
},
create: function(){
game.state.start("PlayGame");
}
}
var playGame = function(game){}
playGame.prototype = {
create: function(){
game.physics.startSystem(Phaser.Physics.ARCADE);
this.canJump = true;
this.isClimbing = false;
this.defineGroups();
this.drawLevel();
this.defineTweens();
game.input.onTap.add(this.handleTap, this);
},
drawLevel: function(){
this.currentFloor = 0;
this.currentLadder = 0;
this.highestFloorY = game.height * gameOptions.floorStart;
this.floorArray = [];
this.ladderArray = [];
while(this.highestFloorY > - 3 * gameOptions.floorGap){
this.addFloor();
if(this.currentFloor > 0){
this.addLadder();
}
this.highestFloorY -= gameOptions.floorGap;
this.currentFloor ++;
}
this.currentFloor = 0;
this.addHero();
},
addFloor: function(){
var floor = game.add.sprite(0, this.highestFloorY, "ground");
this.floorGroup.add(floor);
game.physics.enable(floor, Phaser.Physics.ARCADE);
floor.body.immovable = true;
floor.body.checkCollision.down = false;
this.floorArray.push(floor);
},
addLadder: function(){
var ladder = game.add.sprite(100 + (game.width - 200) * (this.currentFloor % 2), this.highestFloorY, "ladder");
this.ladderGroup.add(ladder);
ladder.anchor.set(0.5, 0);
game.physics.enable(ladder, Phaser.Physics.ARCADE);
ladder.body.immovable = true;
this.ladderArray.push(ladder);
},
addHero: function(){
this.hero = game.add.sprite(game.width / 2, game.height * gameOptions.floorStart - 40, "hero");
this.gameGroup.add(this.hero)
this.hero.anchor.set(0.5, 0);
game.physics.enable(this.hero, Phaser.Physics.ARCADE);
this.hero.body.collideWorldBounds = true;
this.hero.body.gravity.y = gameOptions.playerGravity;
this.hero.body.velocity.x = gameOptions.playerSpeed;
this.hero.body.onWorldBounds = new Phaser.Signal();
this.hero.body.onWorldBounds.add(function(sprite, up, down, left, right){
if(left){
this.hero.body.velocity.x = gameOptions.playerSpeed;
this.hero.scale.x = 1;
}
if(right){
this.hero.body.velocity.x = -gameOptions.playerSpeed;
this.hero.scale.x = -1;
}
}, this)
},
defineTweens: function(){
this.scrollTween = game.add.tween(this.gameGroup).to({
y: gameOptions.floorGap
}, 800, Phaser.Easing.Cubic.Out);
this.scrollTween.onComplete.add(function(){
this.gameGroup.y = 0;
this.floorGroup.forEach(function(item) {
item.y += gameOptions.floorGap;
}, this);
this.ladderGroup.forEach(function(item) {
item.y += gameOptions.floorGap;
}, this);
this.hero.y += gameOptions.floorGap;
}, this)
this.fadeTween = game.add.tween(this.floorArray[0]).to({
alpha: 0
}, 200, Phaser.Easing.Cubic.Out);
this.fadeTween.onComplete.add(function(floor){
floor.y = this.highestFloorY;
floor.alpha =1;
}, this);
},
defineGroups: function(){
this.gameGroup = game.add.group();
this.floorGroup = game.add.group();
this.ladderGroup = game.add.group();
this.gameGroup.add(this.floorGroup);
this.gameGroup.add(this.ladderGroup);
},
handleTap: function(pointer, doubleTap){
if(this.canJump && !this.isClimbing){
this.hero.body.velocity.y = -gameOptions.playerJump;
this.canJump = false;
}
},
update: function(){
this.checkFloorCollision();
this.checkLadderCollision();
this.heroOnLadder();
},
checkFloorCollision: function(){
game.physics.arcade.collide(this.hero, this.floorArray, function(){
this.canJump = true;
}, null, this);
},
checkLadderCollision: function(){
game.physics.arcade.overlap(this.hero, this.ladderArray, function(player, ladder){
if(!this.isClimbing && Math.abs(player.x - ladder.x) < 10){
this.hero.body.velocity.x = 0;
this.hero.body.velocity.y = - gameOptions.climbSpeed;
this.hero.body.gravity.y = 0;
this.isClimbing = true;
this.fadeTween.target = this.floorArray[this.currentFloor];
this.currentFloor = (this.currentFloor + 1) % this.floorArray.length;
this.fadeTween.start();
this.scrollTween.start();
}
}, null, this);
},
heroOnLadder: function(){
if(this.isClimbing && this.hero.y <= this.floorArray[this.currentFloor].y - 40){
this.hero.body.gravity.y = gameOptions.playerGravity;
this.hero.body.velocity.x = gameOptions.playerSpeed * this.hero.scale.x;
this.hero.body.velocity.y = 0;
this.isClimbing = false;
this.fadeTween.target = this.ladderArray[this.currentLadder];
this.fadeTween.start();
this.currentLadder = (this.currentLadder + 1) % this.ladderArray.length;
}
}
}
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.