There are a lot of games (especially hypercasual ones) whose main mechanics depend on circular motion, and there are different ways to achieve this movement. Today, I will show you the method I use. You can copy my method or compare it with other possible methods. Let’s start by looking at what circular motion is:

Click to change direction.

The turning white ball makes a circular movement. The sine and cosine mathematical functions lie at the heart of this concept. So, we can start by looking at what these functions are. Sine and cosine are trigonometric functions that are important for some parts of geometry, calculus, and physics. They are basically the first step to understanding the relationship between angles and sides of triangles.

Sine (sin)

The definition is the ratio of the length of the opposite side to the length of the hypotenuse on the triangle. But basically, in the unit circle, the sine of an angle is the y coordinate of the point. You can understand more about how it works with this interactive tool:

Don’t forget: Because we are working on a unit circle, the maximum length of the opposite side (or triangle height) is 1. The minimum is 0.

Cosine (cos)

Similar to sin, cos is the ratio of the length of the adjacent side to the length of the hypotenuse. But again, in the unit circle, it gives the x coordinate of the point. And you can check cosine with this tool:

Don’t forget: Because we are working on a unit circle, the maximum length of the adjacent side (or triangle width) is 1. The minimum is 0.

Sin & Cos Together

While calculating sin or cos, we will use a unit circle, which is a circle with a radius of 1 unit. Because changes in lengths do not change the value of sin or cos. Only the angle changes the value. But how will we achieve our target circular movement with these functions?

Using sin and cos is actually pretty easy on a computer; you just need to write related math functions like this:

let sin45 = Math.sin(45);
let cos45 = Math.cos(45);

But wait, it’s not the correct way. These functions work with radians, so we need to convert our angles to radians. That is pretty easy; you just need to multiply your angle with PI and divide by 180. So our correct usage will be like this:

let sin45 = Math.sin(45*Math.PI/180);
// result: 0.7071067811865475
let cos45 = Math.cos(45*Math.PI/180);
// result: 0.7071067811865475

You can see that the results seem a little bit different in our interactive examples. That’s because I just round and remove the numbers after 0.00. Now, let’s make an example of how to use them.

We will use a red dot as our source point. And we will take our angle from that point. Also, we will use a green ball as our target. So we will find the green ball x and y coordinates with our sin and cos functions.

So, in the above example, the blue line shows a 45-degree position. And we need to move our green ball to that position. We know our degree is 45. And we know we can find the y coordinate with the sin function and the x coordinate with the cos function. But we already did the above, right? You can check out the previous code example.

Our x and y are around 0.70. But there is one more issue! As you can imagine, 0.70 is a very low value, and our circles can have a 100-pixel radius or even a lot more. For this reason, we need to multiply our result by our circle radius. If we use our code like this, we can move our ball:

let sin45 = Math.sin(45*Math.PI/180);
// result: 0.7071067811865475
let cos45 = Math.cos(45*Math.PI/180);
// result: 0.7071067811865475

ball.x = cos45 * 100;
ball.y = sin45 * 100;

Now, you know how you can rotate our green ball around a circle, right? You just need to change the angle and calculate the new position. Let’s make our first example together using PhaserJS and our new calculation methods.

Get Dirty

Let’s start with creating our game with PhaserJS.

let game = new Phaser.Game({
    type: Phaser.AUTO,
    width: 520,
    height: 300,
    scene: {
        create: create,
        update: update
    }
});

function create(){
    //create game objects
}

function update(time, delta){
    //update game
}

This is just a basic phaser game. We initiate new Phaser. Game object, we gave some width, height, and our scene functions. Probably you already know our create function will work only once, and our update function will continue to work every second. Well, actually 60 times every second, if the game works at 60 fps. If you want to learn more about Phaser, you can check out https://phaser.io/.

Let’s change our create function:

function create(){
    this.xangle     = 0;
    this.yangle     = 0;
    this.direction  = 0;
    this.speed      = 0.003;

    this.gameArea = this.add.graphics({lineStyle: {color: 0x222222, width: 70}});
    this.gameArea.beginPath();
    this.gameArea.arc(0, 0, 200, Phaser.Math.DegToRad(0), Phaser.Math.DegToRad(360), false, 0.02);
    this.gameArea.strokePath();
    this.gameArea.closePath();
    this.gameArea.x = 520;
    this.gameArea.y = 300;

    this.ball = this.add.graphics({fillStyle: {color:0xF0EAE0}});
    this.ball.fillCircle(0, 0, 24);
    this.ball.x = 720;
    this.ball.y = 300;

    this.input.on('pointerdown', ()=>{
        this.direction = !this.direction;
    });
}

The result should look like this:

It’s time to move our circle. Update the update function like this:

function update(){
    let x = (200*Math.cos(this.xangle)) + 520;
    let y = (200*Math.sin(this.yangle)) + 300;
    this.ball.setX(x);
    this.ball.setY(y);
    if(this.direction){
        this.xangle += this.speed * delta;
        this.yangle += this.speed * delta;
    } else {
        this.xangle -= this.speed * delta;
        this.yangle -= this.speed * delta;
    }
}

Our final result should look like this:

I added some particle effects and polished it at the first demo. But they are beyond this blog post. We will end this here, but you can always ask me if you have any questions. See you in the next post!