Get the full commented source code of

HTML5 Suika Watermelon Game

Talking about Circle Race game, Game development, HTML5, Javascript and Phaser.

Some days ago I played “Circle Race“, a HTML5 game built with Construct.

In this easy but fun hyper casual game, you control a ball running around a circle. I said “you control” but actually you don’t control anything. The ball runs on its own, and you can just tap and hold to speed it up.

Too easy? Problems come into play when enemy balls appear running around another circle, overlapping the circle followed by our ball.

If an enemy hits the ball, it’s game over, so it’s up to players to speed up the ball wisely, to avoid collisions.

That’s it. and I managed to build in less than 100 lines, with a lot of room for customization. And also consider I did not use any sprite, all the graphic assets you see in the game, are generated on the fly.

Have a look:

Keep running and avoid red balls. Unlike the original game, when you tap and hold the ball slows down, rahter than accelerating. But obviously the concept is the same.

Have a look at the source code, just 80 lines, with room for customization in gameOptions game object:

let game;
let gameOptions = {
	circleRadius: 300,
	circleStrokeWidth: 20,
	circleDistance: 400,
	ballRadius: 50,
	ballSpeed: [0.5, 2],
	enemySpeedRange: [0.5, 1]
}
window.onload = function() {
    let gameConfig = {
        type: Phaser.AUTO,
		backgroundColor: 0x222222,
        scale: {
            mode: Phaser.Scale.FIT,
            autoCenter: Phaser.Scale.CENTER_BOTH,
            parent: "thegame",
            width: 750,
            height: 1334
        },
        scene: playGame
    }
    game = new Phaser.Game(gameConfig);
    window.focus();
}
class playGame extends Phaser.Scene{
    constructor() {
        super("PlayGame");
    }
    create() {
		this.rounds = 0;
		this.leftSide = false;
		let actualSize = gameOptions.circleStrokeWidth + gameOptions.circleRadius;
		let assetsGraphics = this.make.graphics();
		assetsGraphics.lineStyle(gameOptions.circleStrokeWidth, 0xffffff);
		assetsGraphics.strokeCircle(actualSize, actualSize, gameOptions.circleRadius);
    	assetsGraphics.generateTexture("circle", 2 * actualSize, 2 * actualSize);
		assetsGraphics.clear();
		assetsGraphics.fillStyle(0x00ff00);
		assetsGraphics.fillCircle(gameOptions.ballRadius, gameOptions.ballRadius, gameOptions.ballRadius);
		assetsGraphics.generateTexture("ball", 2 * gameOptions.ballRadius, 2 * gameOptions.ballRadius);
		assetsGraphics.clear();
		assetsGraphics.fillStyle(0xff0000);
		assetsGraphics.fillCircle(gameOptions.ballRadius, gameOptions.ballRadius, gameOptions.ballRadius);
		assetsGraphics.generateTexture("enemyball", 2 * gameOptions.ballRadius, 2 * gameOptions.ballRadius);
    	this.upperCircle = this.add.sprite(game.config.width / 2, game.config.height / 2 - gameOptions.circleDistance / 2, "circle");
		this.lowerCircle = this.add.sprite(game.config.width / 2, game.config.height / 2 + gameOptions.circleDistance / 2, "circle");
		this.ball = this.add.sprite(this.upperCircle.x, this.upperCircle.y - gameOptions.circleRadius, "ball");
		this.ball.radians = - Math.PI / 2;
		this.ball.speed = gameOptions.ballSpeed[1];
		this.enemyGroup = this.add.group();
        this.input.on("pointerdown", function() {
			this.ball.speed = gameOptions.ballSpeed[0];
		}, this);
        this.input.on("pointerup", function() {
			this.ball.speed = gameOptions.ballSpeed[1];
		}, this);
    }
	update(t, dt) {
		this.ball.radians += this.ball.speed * dt / 1000;
		this.ball.x = this.upperCircle.x + gameOptions.circleRadius * Math.cos(this.ball.radians);
		this.ball.y = this.upperCircle.y + gameOptions.circleRadius * Math.sin(this.ball.radians);
		this.enemyGroup.children.iterate(function(enemy) {
			enemy.radians += enemy.speed * dt / 1000;
			enemy.x = this.lowerCircle.x + gameOptions.circleRadius * Math.cos(enemy.radians);
			enemy.y = this.lowerCircle.y + gameOptions.circleRadius * Math.sin(enemy.radians);
			if(Phaser.Math.Distance.Between(this.ball.x, this.ball.y, enemy.x, enemy.y) < gameOptions.ballRadius * 2) {
				this.scene.start("PlayGame");
			}
		}, this);
		let previousLeftSide = this.leftSide;
		this.leftSide = this.ball.x < game.config.width / 2;
		if(previousLeftSide && !this.leftSide) {
			let randomAngle = Phaser.Math.FloatBetween(0, Math.PI * 2);
			let randomSpeed = Phaser.Math.FloatBetween(gameOptions.enemySpeedRange[0], gameOptions.enemySpeedRange[1]);
			let enemy = this.add.sprite(this.lowerCircle.x + gameOptions.circleRadius * Math.cos(randomAngle), this.lowerCircle.y + gameOptions.circleRadius * Math.sin(randomAngle), "enemyball");
			enemy.speed = randomSpeed;
			enemy.radians = randomAngle;
			this.enemyGroup.add(enemy);
		}
	}
}

The code is uncommented but it’s really easy to understand, and it’s just a matter of input and trigonometry. Next time I’ll add some bullet time effect when you slow down the game, 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.