Talking about Endless Runner game, Game development, HTML5, Javascript and Phaser.
Did you like the way you can build endless runner with Unity and the Infinite Runner Engine?
Today we’ll see how to build a basic HTML5 endless runner using Phaser 3, Arcade physics and object pooling. Thanks to object pooling, you won’t need to create more than 5 or 6 platforms during the entire game, although it seems they are infinite.
Moreover, like all endless runners, the player is not running, it’s the entire environement which moves towards the player, giving the feeling the player is running.
Have a look at the result:
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.
This is the source code, with some comments:
let game;
// global game options
let gameOptions = {
platformStartSpeed: 350,
spawnRange: [100, 350],
platformSizeRange: [50, 250],
playerGravity: 900,
jumpForce: 400,
playerStartPosition: 200,
jumps: 2
}
window.onload = function() {
// object containing configuration options
let gameConfig = {
type: Phaser.AUTO,
width: 1334,
height: 750,
scene: playGame,
backgroundColor: 0x444444,
// 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");
this.load.image("player", "player.png");
}
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 and x position
this.addPlatform(game.config.width, game.config.width / 2);
// adding the player;
this.player = this.physics.add.sprite(gameOptions.playerStartPosition, game.config.height / 2, "player");
this.player.setGravityY(gameOptions.playerGravity);
// setting collisions between the player and the platform group
this.physics.add.collider(this.player, this.platformGroup);
// 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){
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, game.config.height * 0.8, "platform");
platform.setImmovable(true);
platform.setVelocityX(gameOptions.platformStartSpeed * -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 ++;
}
}
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;
this.platformGroup.getChildren().forEach(function(platform){
let platformDistance = game.config.width - platform.x - platform.displayWidth / 2;
minDistance = Math.min(minDistance, platformDistance);
if(platform.x < - platform.displayWidth / 2){
this.platformGroup.killAndHide(platform);
this.platformGroup.remove(platform);
}
}, this);
// adding new platforms
if(minDistance > this.nextPlatformDistance){
var nextPlatformWidth = Phaser.Math.Between(gameOptions.platformSizeRange[0], gameOptions.platformSizeRange[1]);
this.addPlatform(nextPlatformWidth, game.config.width + nextPlatformWidth / 2);
}
}
};
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";
}
}
The core of the script is the part where group are defined with callbacks to move platform from active to pooling group and from pooling group to active group.
We can add a lot more options, but I am showing you how to make the game look more interesting next time, meanwhile 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.