Talking about Stick Hero game, Game development, HTML5, Javascript and Phaser.
Like all Ketchapp games, Stick Hero is an easy and fun hyper casual game. The only instructions on the official app page are: “Stretch the stick in order to reach and walk on the platforms. Watch out! If the stick is not long enough, you will fall down! How far can you go?” So in this game you only have to tap and hold the pointer to make a pole grow enough to cross the gap between two platforms, but not too much or you will fall down the other side of the cliff. Thanks to Phaser, a managed to build the game using only tweens. There is not physics in this game. Just a series of animations to be played according to a couple of parameters and player input. Look at the game: Tap and hold to make the pole grow. Release to cross the gap and reach next platform. As said, everything from the scrolling to the pole bounce effect has been managed with tweens. There’s almost no limit in what you can do with tweens as long as serious physics does not come into play. The source code is still uncommented but I tried to make it not that hard to read by splitting it in main methods. The first lines allow a lot of room for customization.
let game;
let gameOptions = {
platformGapRange: [200, 400],
platformWidthRange: [50, 150],
platformHeight: 600,
playerWidth: 32,
playerHeight: 64,
poleWidth: 8,
growTime: 500,
rotateTime: 500,
walkTime: 500,
fallTime: 500,
scrollTime: 250
}
const IDLE = 0;
const WAITING = 1;
const GROWING = 2;
window.onload = function() {
let gameConfig = {
type: Phaser.AUTO,
width: 750,
height: 1334,
scene: [playGame],
backgroundColor: 0x0c88c7
}
game = new Phaser.Game(gameConfig);
window.focus();
resize();
window.addEventListener("resize", resize, false);
}
class playGame extends Phaser.Scene{
constructor(){
super("PlayGame");
}
preload(){
this.load.image("tile", "tile.png");
}
create(){
this.addPlatforms();
this.addPlayer();
this.addPole();
this.input.on("pointerdown", this.grow, this);
this.input.on("pointerup", this.stop, this);
}
addPlatforms(){
this.mainPlatform = 0;
this.platforms = [];
this.platforms.push(this.addPlatform(0));
this.platforms.push(this.addPlatform(game.config.width));
this.tweenPlatform();
}
addPlatform(posX){
let platform = this.add.sprite(posX, game.config.height - gameOptions.platformHeight, "tile");
platform.displayWidth = (gameOptions.platformWidthRange[0] + gameOptions.platformWidthRange[1]) / 2;
platform.displayHeight = gameOptions.platformHeight;
platform.alpha = 0.7;
platform.setOrigin(0, 0);
return platform
}
tweenPlatform(){
let destination = this.platforms[this.mainPlatform].displayWidth + Phaser.Math.Between(gameOptions.platformGapRange[0], gameOptions.platformGapRange[1]);
let size = Phaser.Math.Between(gameOptions.platformWidthRange[0], gameOptions.platformWidthRange[1]);
this.tweens.add({
targets: [this.platforms[1 - this.mainPlatform]],
x: destination,
displayWidth: size,
duration: gameOptions.scrollTime,
callbackScope: this,
onComplete: function(){
this.gameMode = WAITING;
}
})
}
addPlayer(){
this.player = this.add.sprite(this.platforms[this.mainPlatform].displayWidth - gameOptions.poleWidth, game.config.height - gameOptions.platformHeight, "tile");
this.player.displayWidth = gameOptions.playerWidth;
this.player.displayHeight = gameOptions.playerHeight;
this.player.setOrigin(1, 1)
}
addPole(){
this.pole = this.add.sprite(this.platforms[this.mainPlatform].displayWidth, game.config.height - gameOptions.platformHeight, "tile");
this.pole.setOrigin(1, 1);
this.pole.displayWidth = gameOptions.poleWidth;
this.pole.displayHeight = gameOptions.playerHeight / 4;
}
grow(){
if(this.gameMode == WAITING){
this.gameMode = GROWING;
this.growTween = this.tweens.add({
targets: [this.pole],
displayHeight: gameOptions.platformGapRange[1] + gameOptions.platformWidthRange[1],
duration: gameOptions.growTime
});
}
}
stop(){
if(this.gameMode == GROWING){
this.gameMode = IDLE;
this.growTween.stop();
if(this.pole.displayHeight > this.platforms[1 - this.mainPlatform].x - this.pole.x){
this.tweens.add({
targets: [this.pole],
angle: 90,
duration: gameOptions.rotateTime,
ease: "Bounce.easeOut",
callbackScope: this,
onComplete: function(){
if(this.pole.displayHeight < this.platforms[1 - this.mainPlatform].x + this.platforms[1 - this.mainPlatform].displayWidth - this.pole.x){
this.tweens.add({
targets: [this.player],
x: this.platforms[1 - this.mainPlatform].x + this.platforms[1 - this.mainPlatform].displayWidth - this.pole.displayWidth,
duration: gameOptions.walkTime,
callbackScope: this,
onComplete: function(){
this.tweens.add({
targets: [this.player, this.pole, this.platforms[1 - this.mainPlatform], this.platforms[this.mainPlatform]],
props: {
x: {
value: "-= " + this.platforms[1 - this.mainPlatform].x
}
},
duration: gameOptions.scrollTime,
callbackScope: this,
onComplete: function(){
this.prepareNextMove();
}
})
}
})
}
else{
this.platformTooLong();
}
}
})
}
else{
this.platformTooShort();
}
}
}
platformTooLong(){
this.tweens.add({
targets: [this.player],
x: this.pole.x + this.pole.displayHeight + this.player.displayWidth,
duration: gameOptions.walkTime,
callbackScope: this,
onComplete: function(){
this.fallAndDie();
}
})
}
platformTooShort(){
this.tweens.add({
targets: [this.pole],
angle: 90,
duration: gameOptions.rotateTime,
ease: "Cubic.easeIn",
callbackScope: this,
onComplete: function(){
this.tweens.add({
targets: [this.player],
x: this.pole.x + this.pole.displayHeight,
duration: gameOptions.walkTime,
callbackScope: this,
onComplete: function(){
this.tweens.add({
targets: [this.pole],
angle: 180,
duration: gameOptions.rotateTime,
ease: "Cubic.easeIn"
})
this.fallAndDie();
}
})
}
})
}
fallAndDie(){
this.tweens.add({
targets: [this.player],
y: game.config.height + this.player.displayHeight * 2,
duration: gameOptions.fallTime,
ease: "Cubic.easeIn",
callbackScope: this,
onComplete: function(){
this.cameras.main.shake(800, 0.01);
this.time.addEvent({
delay: 2000,
callbackScope: this,
callback: function(){
this.scene.start("PlayGame");
}
})
}
})
}
prepareNextMove(){
this.platforms[this.mainPlatform].x = game.config.width;
this.mainPlatform = 1 - this.mainPlatform;
this.tweenPlatform();
this.pole.angle = 0;
this.pole.x = this.platforms[this.mainPlatform].displayWidth;
this.pole.displayHeight = gameOptions.poleWidth;
}
};
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";
}
}
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.