Talking about Android, Game development, HTML5, Javascript and Phaser.
Did you know Stack the Crates game, based on my Tipsy Tower prototype, has been released? You can play it on your browser or as an Android game on triqui.com, have a look both at the site and at the other games. The physics engine which handles the game is the well known Box2D, which is available for Phaser as a premium plugin. However, Box2D is not the only physics engine you can use in Phaser: p2.js is also available and it comes with the basic Phaser distribution. This is “Stack the Crates” game with p2 physics:If you have a mobile device, you can play the game directly from this link.
You have one minute to drop as many crates as you can while trying to get the highest stack possible.
And this is the source code:
var game;
var gameOptions = {
gameWidth: 640,
gameHeight: 960,
timeLimit: 60,
gravity: 2000,
crateSpeed: 500,
crateHorizontalRange: 540,
fallingHeight: 700,
localStorageName: "stackthecratesgame"
}
var GROUNDHEIGHT;
var CRATEHEIGHT;
window.onload = function() {
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;
var ratio = windowHeight / windowWidth;
if(ratio < 1.5){
gameOptions.gameWidth = gameOptions.gameHeight / ratio;
}
else{
gameOptions.gameHeight = gameOptions.gameWidth * ratio;
}
game = new Phaser.Game(gameOptions.gameWidth, gameOptions.gameHeight, Phaser.CANVAS);
game.state.add("BootGame", bootGame);
game.state.add("PreloadGame", preloadGame);
game.state.add("PlayGame", playGame);
game.state.start("BootGame");
}
var bootGame = function(){};
bootGame.prototype = {
create: function(){
game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
game.scale.pageAlignHorizontally = true;
game.scale.pageAlignVertically = true;
game.stage.disableVisibilityChange = true;
game.state.start("PreloadGame");
}
}
var preloadGame = function(){};
preloadGame.prototype = {
preload: function(){
game.load.image("ground", "assets/sprites/ground.png");
game.load.image("sky", "assets/sprites/sky.png");
game.load.image("crate", "assets/sprites/crate.png");
game.load.image("title", "assets/sprites/title.png");
game.load.image("tap", "assets/sprites/tap.png");
game.load.audio("hit01", ["assets/sounds/hit01.mp3", "assets/sounds/hit01.ogg"]);
game.load.audio("hit02", ["assets/sounds/hit02.mp3", "assets/sounds/hit02.ogg"]);
game.load.audio("hit03", ["assets/sounds/hit03.mp3", "assets/sounds/hit03.ogg"]);
game.load.audio("remove", ["assets/sounds/remove.mp3", "assets/sounds/remove.ogg"]);
game.load.audio("gameover", ["assets/sounds/gameover.mp3", "assets/sounds/gameover.ogg"]);
game.load.bitmapFont("font", "assets/fonts/font.png", "assets/fonts/font.fnt");
game.load.bitmapFont("smallfont", "assets/fonts/smallfont.png", "assets/fonts/smallfont.fnt");
},
create: function(){
GROUNDHEIGHT = game.cache.getImage("ground").height;
CRATEHEIGHT = game.cache.getImage("crate").height;
game.state.start("PlayGame");
}
}
var playGame = function(){};
playGame.prototype = {
create: function(){
if(!Phaser.Device.desktop){
game.scale.forceOrientation(false, true);
game.scale.enterIncorrectOrientation.add(function(){
game.paused = true;
document.querySelector("canvas").style.display = "none";
document.getElementById("wrongorientation").style.display = "block";
})
game.scale.leaveIncorrectOrientation.add(function(){
game.paused = false;
document.querySelector("canvas").style.display = "block";
document.getElementById("wrongorientation").style.display = "none";
})
}
this.lastSoundPlayed = Date.now() ;
this.savedData = localStorage.getItem(gameOptions.localStorageName) == null ? {score : 0} : JSON.parse(localStorage.getItem(gameOptions.localStorageName));
this.hitSound = [game.add.audio("hit01"), game.add.audio("hit02"), game.add.audio("hit03")];
this.gameOverSound = game.add.audio("gameover");
this.removeSound = game.add.audio("remove");
this.score = 0;
this.firstCrate = true;
var sky = game.add.image(0, 0, "sky");
sky.width = game.width;
sky.height = game.height;
this.timeText = game.add.bitmapText(game.width / 2, game.height / 5 + 140, "font", gameOptions.timeLimit.toString(), 144);
this.timeText.alpha = 0.5;
this.timeText.anchor.set(0.5);
this.timeText.visible = false;
this.cameraGroup = game.add.group();
this.crateGroup = game.add.group();
this.cameraGroup.add(this.crateGroup);
game.physics.startSystem(Phaser.Physics.P2JS);
this.collisionGroup = game.physics.p2.createCollisionGroup();
game.physics.p2.setImpactEvents(true);
game.physics.p2.friction = 1;
game.physics.p2.gravity.y = gameOptions.gravity;
this.canDrop = true;
var ground = game.add.sprite(game.width / 2, game.height, "ground");
ground.y = game.height - ground.height / 2;
this.movingCrate = game.add.sprite((game.width - gameOptions.crateHorizontalRange) / 2 , game.height - GROUNDHEIGHT - gameOptions.fallingHeight, "crate");
this.movingCrate.anchor.set(0.5);
this.cameraGroup.add(this.movingCrate);
var crateTween = game.add.tween(this.movingCrate).to({
x: (game.width + gameOptions.crateHorizontalRange) / 2
}, gameOptions.crateSpeed, Phaser.Easing.Linear.None, true, 0, -1, true);
game.physics.p2.enable(ground);
ground.body.static = true;
ground.body.setCollisionGroup(this.collisionGroup);
ground.body.collides(this.collisionGroup);
this.cameraGroup.add(ground);
game.input.onDown.add(this.dropCrate, this);
this.menuGroup = game.add.group();
var tap = game.add.sprite(game.width / 2, game.height / 2, "tap");
tap.anchor.set(0.5);
this.menuGroup.add(tap);
var title = game.add.image(game.width / 2, tap.y - 470, "title");
title.anchor.set(0.5, 0);
this.menuGroup.add(title);
var hiScoreText = game.add.bitmapText(game.width / 2, game.height - 74, "smallfont", "BEST SCORE", 24);
hiScoreText.anchor.set(0.5);
this.menuGroup.add(hiScoreText);
var hiScore = game.add.bitmapText(game.width / 2, game.height - 20, "font", this.savedData.score.toString(), 72);
hiScore.anchor.set(0.5);
this.menuGroup.add(hiScore);
var tapTween = game.add.tween(tap).to({
alpha: 0
}, 150, Phaser.Easing.Cubic.InOut, true, 0, -1, true);
},
dropCrate: function(){
if(this.firstCrate){
this.firstCrate = false;
this.menuGroup.destroy();
this.timer = 0;
this.timerEvent = game.time.events.loop(Phaser.Timer.SECOND, this.tick, this);
this.timeText.visible = true;
}
if(this.canDrop && this.timer <= gameOptions.timeLimit){
this.canDrop = false;
this.movingCrate.alpha = 0;
var fallingCrate = game.add.sprite(this.movingCrate.x, this.movingCrate.y, "crate");
fallingCrate.hit = false;
game.physics.p2.enable(fallingCrate);
this.crateGroup.add(fallingCrate);
fallingCrate.body.setCollisionGroup(this.collisionGroup);
fallingCrate.body.collides(this.collisionGroup, function(b, b2){
var delay = Date.now() - this.lastSoundPlayed;
if(delay > 200 && this.timer <= gameOptions.timeLimit){
this.lastSoundPlayed = Date.now();
Phaser.ArrayUtils.getRandomItem(this.hitSound).play();
}
if(!b.sprite.hit){
b.sprite.hit = true;
this.getMaxHeight();
}
}, this);
}
},
update: function(){
this.crateGroup.forEach(function(i){
if(i.y > game.height + i.height){
if(!i.hit){
this.getMaxHeight();
}
i.destroy();
}
}, this);
},
scaleCamera: function(cameraScale){
var moveTween = game.add.tween(this.cameraGroup).to({
x: (game.width - game.width * cameraScale) / 2,
y: game.height - game.height * cameraScale,
}, 200, Phaser.Easing.Quadratic.IN, true);
var scaleTween = game.add.tween(this.cameraGroup.scale).to({
x: cameraScale,
y: cameraScale,
}, 200, Phaser.Easing.Quadratic.IN, true);
scaleTween.onComplete.add(function(){
this.canDrop = true;
this.movingCrate.alpha = 1;
}, this)
},
getMaxHeight: function(){
var maxHeight = 0
this.crateGroup.forEach(function(i){
if(i.hit){
var height = Math.round((game.height - GROUNDHEIGHT - i.y - CRATEHEIGHT / 2) / CRATEHEIGHT) + 1;
maxHeight = Math.max(height, maxHeight);
}
}, this);
this.movingCrate.y = game.height - GROUNDHEIGHT - maxHeight * CRATEHEIGHT - gameOptions.fallingHeight;
var newHeight = game.height + CRATEHEIGHT * maxHeight;
var ratio = game.height / newHeight;
this.scaleCamera(ratio);
},
tick: function(){
this.timer++;
this.timeText.text = (gameOptions.timeLimit - this.timer).toString()
if(this.timer > gameOptions.timeLimit){
game.time.events.remove(this.timerEvent);
this.movingCrate.destroy();
this.timeText.destroy();
game.time.events.add(Phaser.Timer.SECOND * 2, function(){
this.crateGroup.forEach(function(i){
i.body.static = true;
}, true)
this.removeEvent = game.time.events.loop(Phaser.Timer.SECOND / 10, this.removeCrate, this);
}, this);
}
},
removeCrate: function(){
if(this.crateGroup.children.length > 0){
var tempCrate = this.crateGroup.getChildAt(0);
var height = Math.round((game.height - GROUNDHEIGHT - tempCrate.y - CRATEHEIGHT / 2) / CRATEHEIGHT) + 1;
this.score += height;
this.removeSound.play();
var crateScoreText = game.add.bitmapText(tempCrate.x, tempCrate.y, "smallfont", height.toString(), 36);
crateScoreText.anchor.set(0.5);
this.cameraGroup.add(crateScoreText);
tempCrate.destroy();
}
else{
game.time.events.remove(this.removeEvent);
this.gameOverSound.play();
var scoreText = game.add.bitmapText(game.width / 2, game.height / 5, "font", "YOUR SCORE", 72);
scoreText.anchor.set(0.5);
var scoreDisplayText = game.add.bitmapText(game.width / 2, game.height / 5 + 140, "font", this.score.toString(), 144);
scoreDisplayText.anchor.set(0.5);
localStorage.setItem(gameOptions.localStorageName,JSON.stringify({
score: Math.max(this.score, this.savedData.score)
}));
game.time.events.add(Phaser.Timer.SECOND * 5, function(){
game.state.start("PlayGame");
}, this);
}
}
}
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.