Skip to content
Topic 4 of 5
Theory

Collisions

No collisions — no game

A bullet hit an enemy, the hero landed on a platform, the ball bounced off the paddle, the snake ate an apple — all of this is collision detection: whether two shapes intersect right now. The canvas doesn't know this (it only has pixels), so collisions are calculated from the numbers in the game state.

In most 2D games, objects are simplified to two shapes for collisions: a rectangle and a circle. Even if a complex character is drawn on screen, its "hitbox" is a simple rectangle.

Rectangle with rectangle (AABB)

Rectangles whose sides are parallel to the screen edges are called AABB (axis-aligned bounding box). The check works in reverse: rectangles do not overlap if one is entirely to the left, right, above or below the other. In all other cases they overlap.

function rectsOverlap(a, b) {
  return (
    a.x < b.x + b.w &&    // a's left edge is left of b's right edge
    a.x + a.w > b.x &&    // a's right edge is right of b's left edge
    a.y < b.y + b.h &&    // a's top is above b's bottom
    a.y + a.h > b.y       // a's bottom is below b's top
  );
}

The strict < and > mean that touching at the edges isn't a collision yet. If a game needs it differently, they're changed to <= and >=.

Circle with circle

Circles overlap when the distance between the centers is less than the sum of the radii:

function circlesOverlap(a, b) {
  const dist = Math.hypot(a.x - b.x, a.y - b.y);   // √(dx² + dy²)
  return dist < a.r + b.r;
}

Math.hypot(dx, dy) is the length of the hypotenuse, that is, the distance by the Pythagorean theorem. When there are very many objects, the square root isn't computed and squares are compared instead: dx * dx + dy * dy < (a.r + b.r) ** 2.

Circle with rectangle

A ball and a brick, a coin and a platform. The precise way is to find the point of the rectangle closest to the center of the circle and check the distance to it:

function circleRect(c, r) {
  const nearX = Math.max(r.x, Math.min(c.x, r.x + r.w));
  const nearY = Math.max(r.y, Math.min(c.y, r.y + r.h));
  return Math.hypot(c.x - nearX, c.y - nearY) < c.r;
}

Simple arcade games often do it even more simply: they treat the circle as a square with side 2r and use rectsOverlap. The error is only noticeable at the corners.

Bouncing off walls

The ball must not fly out of the field. We check each wall: if the ball went into it — we put it back on the boundary and turn the velocity around:

if (ball.x - ball.r < 0) {          // left wall
  ball.x = ball.r;
  ball.vx = Math.abs(ball.vx);      // now it definitely flies right
}
if (ball.x + ball.r > canvas.width) {   // right wall
  ball.x = canvas.width - ball.r;
  ball.vx = -Math.abs(ball.vx);     // now it definitely flies left
}

Why not ball.vx = -ball.vx? In one frame the ball can get deep into a wall. If you only flip the velocity, on the next frame it may not be out yet — and the velocity flips again, the ball "sticks" and jitters. Putting it back on the boundary plus Math.abs make the bounce reliable.

What to do after a collision

A collision is only a "yes / no". Then the game decides:

for (const enemy of enemies) {
  if (rectsOverlap(bullet, enemy)) {
    enemy.alive = false;
    bullet.alive = false;
    score += 10;
  }
}
enemies = enemies.filter((e) => e.alive);

Removing from an array right while looping over it is dangerous (the indices shift), so first objects are marked alive = false, and then removed with filter.

Objects that are too fast

If a bullet flies 50 pixels per frame and a wall is 10 thick, in one frame the bullet is still in front of the wall, and in the next it's already behind it, and no intersection happens. This is called "tunneling". Simple remedies: limit the speed, make objects thicker, or move fast objects in several small steps per frame.

Summary

  • Hitboxes are rectangles and circles; we check numbers from the state, not pixels.
  • AABB: four edge comparisons; circles: Math.hypot(dx, dy) < r1 + r2.
  • Bounce: put back on the boundary and set the direction with Math.abs.
  • After a collision, mark the objects and remove them with filter.

Do the Rectangles Overlap?

Easy+10 XP

Write a function rectsOverlap(a, b). The rectangles are given as { x, y, w, h } (the top-left corner and size). The function returns true if the rectangles share some area. If they only touch at the edges, that's not an overlap.

rectsOverlap({x:0,y:0,w:10,h:10}, {x:5,y:5,w:10,h:10})    → true
rectsOverlap({x:0,y:0,w:10,h:10}, {x:10,y:0,w:5,h:5})     → false (touching)

Connect the connector to solve tasks

Task code runs on your own computer — faster and more reliable than in the browser. This needs the free CodeWithMe connector app. You only connect it once.

Looking for the connector on this computer…

  1. 1
    Download the connector for your system (Windows, macOS or Linux).Download the connectorOn the download page pick the file with your system in its name, e.g. windows-amd64.
  2. 2Run it and choose a folder for your solutions. It will install Python and Node.js there by itself when needed.
  3. 3
    Enter below the 6-digit code shown in the connector window (“Code for CodeWithMe”). If the browser asks about local network access — allow it.

The connector is open source (on GitHub), works only on your computer and runs code only after you enter the pairing code.