Talking about Endless Runner game, Game development, HTML5, Javascript and Phaser.
Did you play the endless runner prototype I published last week?
It was a very basic prototype but I was able to create a playable game in a very few lines.
What can we do with a few more lines?
Well, I added player animation – not we have a running character rather than a rectangle – and more customization such as platforms at different heights and with different speed. Remember in endless runners the player never runs, it’s the entire environment which moves towards the player.
Have a look at the game:
Click or tap to jump. You can also double jump and you can set the game to make any number of jumps.
Fall down and it’s game over.
I also improved the source code, adding more comments and above all commenting all customizable options in gameOptions
global object.
let game;
// global game options
let gameOptions = {
// platform speed range, in pixels per second
platformSpeedRange: [300, 400],
// spawn range, how far should be the rightmost platform from the right edge
// before next platform spawns, in pixels
spawnRange: [80, 300],
// platform width range, in pixels
platformSizeRange: [90, 300],
// a height range between rightmost platform and next platform to be spawned
platformHeightRange: [-10, 10],
// a scale to be multiplied by platformHeightRange
platformHeighScale: 10,
// platform max and min height, as screen height ratio
platformVerticalLimit: [0.4, 0.8],
// player gravity
playerGravity: 900,
// player jump force
jumpForce: 400,
// player starting X position
playerStartPosition: 200,
// consecutive jumps allowed
jumps: 2
}
window.onload = function() {
// object containing configuration options
let gameConfig = {
type: Phaser.AUTO,
width: 1334,
height: 750,
scene: playGame,
backgroundColor: 0x87CEEB,
// physics settings
physics: {
default: "arcade"
}
}
game = new Phaser.Game(gameConfig);
window.focus();
resize();
window.addEventListener("resize", resize, false);
}
// playGame scene
class playGame extends Phaser.Scene{
constructor(){
super("PlayGame");
}
preload(){
this.load.image("platform", "platform.png");
// player is a sprite sheet made by 24x48 pixels
this.load.spritesheet("player", "player.png", {
frameWidth: 24,
frameHeight: 48
});
}
create(){
// group with all active platforms.
this.platformGroup = this.add.group({
// once a platform is removed, it's added to the pool
removeCallback: function(platform){
platform.scene.platformPool.add(platform)
}
});
// pool
this.platformPool = this.add.group({
// once a platform is removed from the pool, it's added to the active platforms group
removeCallback: function(platform){
platform.scene.platformGroup.add(platform)
}
});
// number of consecutive jumps made by the player
this.playerJumps = 0;
// adding a platform to the game, the arguments are platform width, x position and y position
this.addPlatform(game.config.width, game.config.width / 2, game.config.height * gameOptions.platformVerticalLimit[1]);
// adding the player;
this.player = this.physics.add.sprite(gameOptions.playerStartPosition, game.config.height * 0.7, "player");
this.player.setGravityY(gameOptions.playerGravity);
// setting player animation
this.anims.create({
key: "run",
frames: this.anims.generateFrameNumbers("player", {
start: 0,
end: 1
}),
frameRate: 8,
repeat: -1
});
// setting collisions between the player and the platform group
this.physics.add.collider(this.player, this.platformGroup, function(){
// play "run" animation if the player is on a platform
if(!this.player.anims.isPlaying){
this.player.anims.play("run");
}
}, null, this);
// checking for input
this.input.on("pointerdown", this.jump, this);
}
// the core of the script: platform are added from the pool or created on the fly
addPlatform(platformWidth, posX, posY){
let platform;
if(this.platformPool.getLength()){
platform = this.platformPool.getFirst();
platform.x = posX;
platform.active = true;
platform.visible = true;
this.platformPool.remove(platform);
}
else{
platform = this.physics.add.sprite(posX, posY, "platform");
platform.setImmovable(true);
platform.setVelocityX(Phaser.Math.Between(gameOptions.platformSpeedRange[0], gameOptions.platformSpeedRange[1]) * -1);
this.platformGroup.add(platform);
}
platform.displayWidth = platformWidth;
this.nextPlatformDistance = Phaser.Math.Between(gameOptions.spawnRange[0], gameOptions.spawnRange[1]);
}
// the player jumps when on the ground, or once in the air as long as there are jumps left and the first jump was on the ground
jump(){
if(this.player.body.touching.down || (this.playerJumps > 0 && this.playerJumps < gameOptions.jumps)){
if(this.player.body.touching.down){
this.playerJumps = 0;
}
this.player.setVelocityY(gameOptions.jumpForce * -1);
this.playerJumps ++;
// stops animation
this.player.anims.stop();
}
}
update(){
// game over
if(this.player.y > game.config.height){
this.scene.start("PlayGame");
}
this.player.x = gameOptions.playerStartPosition;
// recycling platforms
let minDistance = game.config.width;
let rightmostPlatformHeight = 0;
this.platformGroup.getChildren().forEach(function(platform){
let platformDistance = game.config.width - platform.x - platform.displayWidth / 2;
if(platformDistance < minDistance){
minDistance = platformDistance;
rightmostPlatformHeight = platform.y;
}
if(platform.x < - platform.displayWidth / 2){
this.platformGroup.killAndHide(platform);
this.platformGroup.remove(platform);
}
}, this);
// adding new platforms
if(minDistance > this.nextPlatformDistance){
let nextPlatformWidth = Phaser.Math.Between(gameOptions.platformSizeRange[0], gameOptions.platformSizeRange[1]);
let platformRandomHeight = gameOptions.platformHeighScale * Phaser.Math.Between(gameOptions.platformHeightRange[0], gameOptions.platformHeightRange[1]);
console.log(rightmostPlatformHeight)
let nextPlatformGap = rightmostPlatformHeight + platformRandomHeight;
let minPlatformHeight = game.config.height * gameOptions.platformVerticalLimit[0];
let maxPlatformHeight = game.config.height * gameOptions.platformVerticalLimit[1];
let nextPlatformHeight = Phaser.Math.Clamp(nextPlatformGap, minPlatformHeight, maxPlatformHeight);
this.addPlatform(nextPlatformWidth, game.config.width + nextPlatformWidth / 2, nextPlatformHeight);
}
}
};
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";
}
}
We can add a lot more stuff, such as coins and enemies – what about a patrolling enemy walking on platforms – but that’s all at the moment, next week I will show you a big update and hopefully more comments on the script. 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.