Get the full commented source code of

HTML5 Suika Watermelon Game

Talking about Circular endless runner game, Game development, HTML5, Javascript and Phaser.

Did you enjoy my posts about the circular endless runner prototype? When I was playing with it, a doubt came into my mind: how to create endless obstacles? Or coins? In ordinary horizontal or vertical endless runners, it’s quite easy because they appear from the right or the top of the stage, then disappear to the opposite side. Troubles knocks your door when you have a circular endless runner, where obstacles – spikes in this case – won’t leave the stage. So this is what I made: when a spike is placed on the stage – or reused, if you love object pooling – a custom property called approaching is set to false. Once the angle between the player and the spike is less than a given value, it means the player is approaching the spike, so approaching property is set to true. As the player keeps running, the angle between the player and the spike will get smaller and smaller, then it will start getting bigger once the player jumped over the spike. At this time, the spike can be removed and placed elsewhere. I just wait for the player to be at a certain distance before removing the spike, so I will be waiting for the angle between the spike and the player to be greater than a given value if approaching is true. At this time the spike is removed and placed elsewhere. Look at the prototype:
Click or tap to jump and double jump – won’t check for collisions at the moment, just look how spikes are removed and placed elsewhere as the player keeps running. And this is the source code, still uncommented but easy to understand:
var game;

var gameOptions = {
    bigCircleRadius: 250,
    playerRadius: 25,
    playerSpeed: 1,
    worldGravity: 0.8,
    jumpForce: [12, 8],
    spikeSize: [25, 50],
    closeToSpike: 10,
    farFromSpike: 25
}

window.onload = function() {
    var gameConfig = {
        thpe: Phaser.CANVAS,
        width: 800,
        height: 800,
        scene: [playGame]
    }
    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("bigcircle", "bigcircle.png");
        this.load.image("player", "player.png");
        this.load.image("spike", "spike.png");
    }
    create(){
        this.bigCircle = this.add.sprite(game.config.width / 2, game.config.height / 2, "bigcircle");
        this.bigCircle.displayWidth = gameOptions.bigCircleRadius * 2;
        this.bigCircle.displayHeight = gameOptions.bigCircleRadius * 2;
        this.player = this.add.sprite(game.config.width / 2, game.config.height / 2 - gameOptions.bigCircleRadius - gameOptions.playerRadius, "player");
        this.player.displayWidth = gameOptions.playerRadius * 2;
        this.player.displayHeight = gameOptions.playerRadius * 2;
        this.player.currentAngle = -90;
        this.player.jumpOffset = 0;
        this.player.jumps = 0;
        this.player.jumpForce = 0;
        this.spikeGroup = this.add.group();
        this.input.on("pointerdown", function(e){
            if(this.player.jumps < 2){
                this.player.jumps ++;
                this.player.jumpForce = gameOptions.jumpForce[this.player.jumps - 1];
            }
        }, this);
        for(var i = 0; i < 6; i ++){
            var spike = this.add.sprite(0, 0, "spike");
            spike.setOrigin(0, 0.5);
            this.spikeGroup.add(spike);
            this.placeSpike(spike, Math.floor(i / 2));
        }
    }
    placeSpike(spike, quadrant){
        var randomAngle = Phaser.Math.Between(quadrant * 90, (quadrant + 1) * 90);
        randomAngle = Phaser.Math.Angle.WrapDegrees(randomAngle);
        var randomAngleRadians = Phaser.Math.DegToRad(randomAngle);
        var spikeX = this.bigCircle.x + (gameOptions.bigCircleRadius - 4) * Math.cos(randomAngleRadians);
        var spikeY = this.bigCircle.y + (gameOptions.bigCircleRadius - 4) * Math.sin(randomAngleRadians);
        spike.x = spikeX;
        spike.y = spikeY;
        spike.quadrant = quadrant;
        spike.angle = randomAngle;
        spike.approaching = false;
    }
    update(){
        if(this.player.jumps > 0){
            this.player.jumpOffset += this.player.jumpForce;
            this.player.jumpForce -= gameOptions.worldGravity;
            if(this.player.jumpOffset < 0){
                this.player.jumpOffset = 0;
                this.player.jumps = 0;
                this.player.jumpForce = 0;
            }
        }
        this.player.currentAngle = Phaser.Math.Angle.WrapDegrees(this.player.currentAngle + gameOptions.playerSpeed);
        var radians = Phaser.Math.DegToRad(this.player.currentAngle);
        var distanceFromCenter = (gameOptions.bigCircleRadius * 2 + gameOptions.playerRadius * 2) / 2 + this.player.jumpOffset;
        this.player.x = this.bigCircle.x + distanceFromCenter * Math.cos(radians);
        this.player.y = this.bigCircle.y + distanceFromCenter * Math.sin(radians);
        var revolutions = (gameOptions.bigCircleRadius * 2) / (gameOptions.playerRadius * 2) + 1;
        this.player.angle = this.player.currentAngle * revolutions;
        this.spikeGroup.children.iterate(function(spike){
            var angleDiff = this.getAngleDifference(spike.angle, this.player.currentAngle);
            if(!spike.approaching && angleDiff < gameOptions.closeToSpike){
                spike.approaching = true;
            }
            if(spike.approaching && angleDiff > gameOptions.farFromSpike){
                this.placeSpike(spike, (spike.quadrant + 3) % 4);
            }
        }, this)
    }
    getAngleDifference(a1, a2){
        var angleDifference = a1 - a2
        angleDifference += (angleDifference > 180) ? -360 : (angleDifference < -180) ? 360 : 0
        return Math.abs(angleDifference);
    }
}

// pure javascript to scale the game
function resize() {
    var canvas = document.querySelector("canvas");
    var windowWidth = window.innerWidth;
    var windowHeight = window.innerHeight;
    var windowRatio = windowWidth / windowHeight;
    var 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";
    }
}
Next time, I will show you how to check for collisions without using physics engines, do you have any clue? I have. 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.