Do you like my tutorials?

Then consider supporting me on Ko-fi

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

August 2018 will be remembered as the month of the Circular Endless Runner prototype. Time to update the Phaser version adding collision detection. Yesterday in the Unity version of the prototype I wrote a script which forgives a lot of player errors, actually checking for collisions between the player and a single point of each spike. Phaser version will be harder, because I will be checking shape collision between the player and the spikes. How can we check for shape collision without physics? There won’t be any “shape” collision, we are going to check for line segment Vs circle like I did in this example. There are only two sides of each triangle which can collide with the circle, and I am showing you when to check for collision by painting them red in this prototype:
Click or tap to jump and double jump, see how spike sides turn red when it’s time to check for collision. And this is the script, uncommented because a bit confused but I am going to fix – and comment – next time.
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.graphics = this.add.graphics();
        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.top = new Phaser.Math.Vector2(spikeX + gameOptions.spikeSize[1] * Math.cos(randomAngleRadians), spikeY + gameOptions.spikeSize[1] * Math.sin(randomAngleRadians));
        spike.base1 = new Phaser.Math.Vector2(spikeX + gameOptions.spikeSize[0] / 2 * Math.cos(randomAngleRadians + Math.PI / 2), spikeY + gameOptions.spikeSize[0] / 2 * Math.sin(randomAngleRadians + Math.PI / 2));
        spike.base2 = new Phaser.Math.Vector2(spikeX + gameOptions.spikeSize[0] / 2 * Math.cos(randomAngleRadians - Math.PI / 2), spikeY + gameOptions.spikeSize[0] / 2 * Math.sin(randomAngleRadians - Math.PI / 2));
        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.graphics.clear();
        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){
                this.graphics.lineStyle(4, 0xff0000);
                this.graphics.beginPath();
                this.graphics.moveTo(spike.top.x, spike.top.y);
                this.graphics.lineTo(spike.base1.x, spike.base1.y);
                this.graphics.closePath();
                this.graphics.strokePath();
                this.graphics.beginPath();
                this.graphics.moveTo(spike.top.x, spike.top.y);
                this.graphics.lineTo(spike.base2.x, spike.base2.y);
                this.graphics.closePath();
                this.graphics.strokePath();
                if(this.distToSegmentSquared(new Phaser.Math.Vector2(this.player.x, this.player.y), gameOptions.playerRadius, spike.top, spike.base1) || this.distToSegmentSquared(new Phaser.Math.Vector2(this.player.x, this.player.y), gameOptions.playerRadius, spike.top, spike.base2)){
                    this.scene.start("PlayGame");
                }
                if(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);
    }
    getDistance(p1, p2){
        return (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y);
    }
    distToSegmentSquared(circleCenter, circleRadius, segmentStart, segmentEnd){
        var l2 = this.getDistance(segmentStart, segmentEnd);
        var t = ((circleCenter.x - segmentStart.x) * (segmentEnd.x - segmentStart.x) + (circleCenter.y - segmentStart.y) * (segmentEnd.y - segmentStart.y)) / l2;
        t = Math.max(0, Math.min(1, t));
        var tX = segmentStart.x + t * (segmentEnd.x - segmentStart.x);
        var tY = segmentStart.y + t * (segmentEnd.y - segmentStart.y);
        var tPoint = {
            x: tX,
            y: tY
        }
        return this.getDistance(circleCenter, tPoint) < circleRadius * circleRadius;
    }
}

// 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";
    }
}
Our physics game without physics engines is almost completed, download the source code and stay tuned for next big update.

Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.