How to code collision detection using Javascript a step-by-step guide.

How to code collision detection using Javascript a step-by-step guide.


Understanding Collision Detection in HTML Canvas

When creating games or interactive animations, one of the key concepts you'll encounter is collision detection. Whether you're designing a simple ball bouncing game or a complex multiplayer experience, understanding how to detect and respond to collisions is crucial.

In this article, we'll delve into the principles of collision detection, particularly in the context of HTML canvas, and provide a practical example featuring a rectangle and a circle.

What is collisiion?

Collision is an event where two or more objects come into contact or intersect. In computer graphics and games, it involves detecting when shapes like circles or rectangles overlap, allowing for realistic interactions like bouncing, stopping, or triggering events.

The Principle of Collision Detection

There are various methods to detect collisions, depending on the shapes and the level of precision required. The most common techniques include:

  1. Bounding Box Collision: This method uses the simplest enclosing rectangles around objects, known as bounding boxes, to check for overlaps. It's efficient but may not be precise for irregular shapes.
  2. Circle Collision: Useful for circular objects, this method checks if the distance between the centers of two circles is less than the sum of their radii.
  3. Pixel-Perfect Collision: A more precise but computationally expensive method that checks for overlapping pixels between objects.

In this article, we'll focus on a practical example using the bounding box and circle collision methods to detect interactions between a rectangle and a circle on the HTML canvas.

What we will achieve:


Before collision, both objects will move at random velocity in the canvas


After collision, they will change colour.

HTML and CSS Setup

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Collision Detection</title>
    <style>
        body, html {
            margin: 0;
            padding: 0;
            overflow: hidden;
            background-color: #000;
        }
        canvas {
            display: block;
            background-color: #111;
        }
    </style>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<script src="collision.js"></script>
</body>
</html>        

In this setup, we define a basic HTML structure with a canvas element, styled to occupy the full screen and have a dark background. The collision.js script will contain the main logic for our collision detection.

JavaScript: Rect Class

Rect is the class that we will use to create the rectangle object on our HTML canvas. This class will have a constructor, a draw() method and an update() method along with a getBounds() utility method. We will look into each of them one by one.

1. Constructor

class Rect {
    constructor(x, y, width, height, color) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
        this.color = color;
        this.velocity = {
            x: random(-3, 3),
            y: random(-3, 3)
        };
    }
}        

Explanation:

  • x, y: The coordinates of the top-left corner of the rectangle. They define the rectangle's position on the canvas.
  • width, height: The dimensions of the rectangle.
  • color: The fill color of the rectangle.
  • velocity: An object representing the speed and direction of the rectangle's movement. It has two properties, x and y, initialized with random values between -3 and 3. This controls how fast and in which direction the rectangle moves along the x and y axes.

2. draw() Method

draw() {
    ctx.fillStyle = this.color;
    ctx.fillRect(this.x, this.y, this.width, this.height);
}        

Explanation:

  • ctx.fillStyle = this.color: Sets the fill color for the rectangle.
  • ctx.fillRect(this.x, this.y, this.width, this.height): Draws the rectangle on the canvas at the position (x, y) with the specified width and height.

The draw() method is responsible for rendering the rectangle on the canvas using its current properties.

3. update() Method

update() {
    this.x += this.velocity.x;
    this.y += this.velocity.y;

    // Check for collision with canvas edges
    if (this.x + this.width > canvas.width || this.x < 0) {
        this.velocity.x = -this.velocity.x;
    }

    if (this.y + this.height > canvas.height || this.y < 0) {
        this.velocity.y = -this.velocity.y;
    }

    this.draw();
}        

Explanation:

  • this.x += this.velocity.x and this.y += this.velocity.y: Update the rectangle's position by adding the velocity to the current coordinates. This moves the rectangle on the canvas.
  • Collision with canvas edges:if (this.x + this.width > canvas.width || this.x < 0): Checks if the rectangle has hit the right or left edge of the canvas. If so, it reverses the x-velocity (this.velocity.x = -this.velocity.x), causing the rectangle to bounce back.if (this.y + this.height > canvas.height || this.y < 0): Checks if the rectangle has hit the bottom or top edge of the canvas. If so, it reverses the y-velocity (this.velocity.y = -this.velocity.y), causing the rectangle to bounce back.
  • this.draw(): Calls the draw() method to render the rectangle at its updated position.

How velocity works?

The velocity object controls the speed and direction of the rectangle's movement. It has two properties:

  • velocity.x: The change in the x-coordinate per update. A positive value moves the rectangle to the right, while a negative value moves it to the left.
  • velocity.y: The change in the y-coordinate per update. A positive value moves the rectangle downwards, while a negative value moves it upwards.

By updating the x and y positions with these velocity values in the update() method, the rectangle moves across the canvas. The collision detection with the canvas edges reverses the respective velocity, creating a bouncing effect.

4. getBounds() Method

getBounds() {
        return {
            left: this.x,
            right: this.x + this.width,
            top: this.y,
            bottom: this.y + this.height
        };
    }        

Really simple logic. The getBounds method returns an object with the pixel values of it's edges.

Thus moving ahead with the circle class it has similar logic as the rect class with only difference being the draw() method.

JavaScript: Circle Class

Circle is the class that we will use to create the circle object on our HTML canvas. This class will have a constructor, a draw() method and an update() method along with a getBounds() utility method. We will look into each of them one by one.

1. Constructor

class Circle {
    constructor(x, y, radius, color) {
        this.x = x;
        this.y = y;
        this.radius = radius;
        this.color = color;
        this.velocity = {
            x: random(-3, 3),
            y: random(-3, 3)
        };
    }
}        

Explanation:

  • x, y: The coordinates of the center of the circle. They define the circle's position on the canvas.
  • radius: The radius of the circle, determining its size.
  • color: The fill color of the circle.
  • velocity: An object representing the speed and direction of the circle's movement. It has two properties, x and y, initialized with random values between -3 and 3. This controls how fast and in which direction the circle moves along the x and y axes.

2. draw() Method

draw() {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
    ctx.fillStyle = this.color;
    ctx.fill();
    ctx.closePath();
}        

  • ctx.beginPath(): Begins a new path, which is a set of points that define a shape.
  • ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false): Draws an arc (which, with a full circle, represents the circle). The parameters specify the circle's center, radius, start angle (0), end angle (Math.PI * 2 for a full circle), and the direction (false for clockwise).
  • ctx.fillStyle = this.color: Sets the fill color for the circle.
  • ctx.fill(): Fills the circle with the specified color.
  • ctx.closePath(): Closes the path, which helps to ensure the shape is correctly rendered.

The draw() method is responsible for rendering the circle on the canvas using its current properties.

3. update() Method

update() {
    this.x += this.velocity.x;
    this.y += this.velocity.y;

    // Check for collision with canvas edges
    if (this.x + this.radius > canvas.width || this.x - this.radius < 0) {
        this.velocity.x = -this.velocity.x;
    }

    if (this.y + this.radius > canvas.height || this.y - this.radius < 0) {
        this.velocity.y = -this.velocity.y;
    }

    this.draw();
}        

Explanation:

  • this.x += this.velocity.x and this.y += this.velocity.y: Update the circle's position by adding the velocity to the current coordinates. This moves the circle on the canvas.
  • Collision with canvas edges:if (this.x + this.radius > canvas.width || this.x - this.radius < 0): Checks if the circle has hit the right or left edge of the canvas. If so, it reverses the x-velocity (this.velocity.x = -this.velocity.x), causing the circle to bounce back.if (this.y + this.radius > canvas.height || this.y - this.radius < 0): Checks if the circle has hit the bottom or top edge of the canvas. If so, it reverses the y-velocity (this.velocity.y = -this.velocity.y), causing the circle to bounce back.
  • this.draw(): Calls the draw() method to render the circle at its updated position.

4. getBounds() Method

getBounds() {
    return {
        left: this.x - this.radius,
        right: this.x + this.radius,
        top: this.y - this.radius,
        bottom: this.y + this.radius
    };
}        

Explanation

The getBounds() method calculates the edges of the circle based on its center coordinates (x, y) and its radius:

  • left: This is the x-coordinate of the left edge of the circle. It is calculated by subtracting the radius from the x-coordinate of the circle's center (this.x). This gives the smallest x-value that the circle occupies.
  • right: This is the x-coordinate of the right edge of the circle. It is found by adding the radius to the x-coordinate of the circle's center (this.x). This represents the largest x-value that the circle occupies.
  • top: This is the y-coordinate of the top edge of the circle. It is obtained by subtracting the radius from the y-coordinate of the circle's center (this.y). This indicates the smallest y-value that the circle occupies.
  • bottom: This is the y-coordinate of the bottom edge of the circle. It is determined by adding the radius to the y-coordinate of the circle's center (this.y). This provides the largest y-value that the circle occupies.

Starting the Animation engine

Now that we have coded our objects, It's time to look into coding our animation loop and create the instance of the Rect and Circle classes.

// Initialize rectangle and circle
const rect = new Rect(100, 100, 100, 100, 'red');
const circle = new Circle(300, 300, 50, 'blue');

// Animation loop
function animate() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    rect.update();
    circle.update();

    requestAnimationFrame(animate);
}

animate();         

You must have noticed that we have still not checked the collision between the rectange and circle. So let's code a simple function that will check the collision in each animation frame.

The Collision detection function:

The checkCollision function checks if a rectangle and a circle collide on a canvas. It calculates the closest point on the rectangle to the circle's center, then determines if the distance from this point to the circle's center is less than the circle's radius. Here's a detailed explanation of each part:

Function definition and Setup

function checkCollision(rect, circle) {
    const rectBounds = rect.getBounds();
    const circleBounds = circle.getBounds();        

Finding the Closest Point on the Rectangle to the Circle's Center

    // Closest point on the rectangle to the circle
    const closestX = Math.max(rectBounds.left, Math.min(circle.x, rectBounds.right));
    const closestY = Math.max(rectBounds.top, Math.min(circle.y, rectBounds.bottom));        

  • closestX: The closest x-coordinate on the rectangle to the circle's center (circle.x). It is found by:
  • closestY: The closest y-coordinate on the rectangle to the circle's center (circle.y). It is found similarly to closestX by clamping the circle's y-coordinate within the top and bottom edges of the rectangle.

Calculating the Distance from the Closest Point to the Circle's Center

    // Distance from closest point to the circle center
    const distanceX = circle.x - closestX;
    const distanceY = circle.y - closestY;        

  • distanceX: The horizontal distance between the circle's center and the closest point on the rectangle.
  • distanceY: The vertical distance between the circle's center and the closest point on the rectangle.

Collision Detection

    return Math.sqrt(distanceX ** 2 + distanceY ** 2) < circle.radius;        

  • Math.sqrt(distanceX ** 2 + distanceY ** 2): This calculates the Euclidean distance between the circle's center and the closest point on the rectangle using the Pythagorean theorem.
  • < circle.radius: The function returns true if this distance is less than the circle's radius, indicating a collision. Otherwise, it returns false.

Now that we have implemented the collision detection function. We will include it in our animation frame.

function animate() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    rect.update();
    circle.update();

   //Include collision detection
    if (checkCollision(rect, circle)) {
        rect.color = 'green';
        circle.color = 'yellow';
    } else {
        rect.color = 'red';
        circle.color = 'blue';
    }

    requestAnimationFrame(animate);
}        

AND THAT'S IT!!

That is all you need in order to implement a simple collision detection.

Thanks for reading this article. This was my first ever article on LinkedIn. I would greatly appreciate your feedback. Peace.



Bhargav bedekar

Pursuing a dual degree: BSc Data Science and AI at IIT Madras + Bachelor of Engineering at New LJ Institute. Passionate about AI and engineering, exploring innovative intersections in tech. Let’s connect!

7 个月

Insightful!

Rahil Dasadia

Student at New L.J institute of engineering and technology

7 个月

Superb??

回复
Shashank Nakka

Data Science Intern at RESOLUTE CORP BHARAT PRIVATE LIMITED | Data Science | Azure Machine Learning | Deep Learning | GenAI | Computer vision | Python Developer | Flutter Android Development |

7 个月

Excellent!!

Prathmesh Pandya

Intern at Infopercept

7 个月

Great work!!

要查看或添加评论,请登录

社区洞察

其他会员也浏览了