← Collision detection for beginners

[Chapter 2 · Part I · 2D]

Distance between two points

The distance formula on a line and in the plane, straight from Pythagoras' theorem, and the trick that lets collision tests skip the square root.

Most collision tests come down to one question: how far apart are two things?

Along a line

The distance between two points A(x1)A(x_{1}) and B(x2)B(x_{2}) on a line is equal to the absolute value of the difference of their coordinates.

AB=x1x2=(x1x2)2|AB| = |x_{1} - x_{2}| = \sqrt{(x_{1} - x_{2})^2}

Try moving your cursor along this grid to interact with this example.

Move the pointer over the grid.
TypeScript
function distanceOnLine(x1: number, x2: number): number {
	return Math.abs(x1 - x2);
}
JavaScript
function distanceOnLine(x1, x2) {
	return Math.abs(x1 - x2);
}
Python
def distance_on_line(x1: float, x2: float) -> float:
    return abs(x1 - x2)
distanceOnLine
time O(1) space O(1)

In the plane

The distance between two points A(x1,y1)A(x_{1}, y_{1}) and B(x2,y2)B(x_{2}, y_{2}) in the plane is the hypotenuse of the right triangle ABCABC, where CC is level with AA and in line with BB: AC=x2x1|AC| = |x_{2} - x_{1}|, BC=y2y1|BC| = |y_{2} - y_{1}|. Pythagoras’ theorem says AB2=AC2+BC2|AB|^2 = |AC|^2 + |BC|^2, so

AB=(x2x1)2+(y2y1)2|AB| = \sqrt{(x_{2} - x_{1})^2 + (y_{2} - y_{1})^2}
Move the pointer over the grid.
TypeScript
type Point = { x: number; y: number };

function distance(a: Point, b: Point): number {
	return Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2);
}
JavaScript
// A point is an object like { x: 4, y: 3 }.
function distance(a, b) {
	return Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2);
}
Python
import math
from dataclasses import dataclass


@dataclass
class Point:
    x: float
    y: float


def distance(a: Point, b: Point) -> float:
    return math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2)
distance
time O(1) space O(1)

JavaScript’s Math.hypot and Python’s math.hypot do the squaring, adding and square root for you: pass them the two differences and they return the distance. They’re also careful with very large and very small numbers, where squaring them first could overflow or round down to zero.

TypeScript
function distance(a: Point, b: Point): number {
	return Math.hypot(b.x - a.x, b.y - a.y);
}
JavaScript
function distance(a, b) {
	return Math.hypot(b.x - a.x, b.y - a.y);
}
Python
def distance(a: Point, b: Point) -> float:
    return math.hypot(b.x - a.x, b.y - a.y)
distance
time O(1) space O(1)

Skipping the square root

The square root is the slowest part of that function, and a collision test rarely needs the distance itself. It only needs to know whether the distance is smaller than something. Both sides of that comparison are never negative, so squaring them doesn’t change the answer:

dr    d2r2d \le r \iff d^2 \le r^2

Try it below. The point PP is inside the circle when its distance dd from the centre is at most the radius rr. The gauges on the right compare dd with rr, and d2d^2 with r2r^2. d2d^2 grows much faster than dd, but it passes r2r^2 at exactly the moment dd passes rr, so both comparisons always agree, and only one of them needs a square root.

dSd vs rrd² vs r²P
Drag P. d² grows much faster than d, but both cross their mark at the same moment. d = 107 > r = 100 · d² = 11450 > r² = 10000 → outside either way
TypeScript
function distanceSquared(a: Point, b: Point): number {
	return (b.x - a.x) ** 2 + (b.y - a.y) ** 2;
}
JavaScript
function distanceSquared(a, b) {
	return (b.x - a.x) ** 2 + (b.y - a.y) ** 2;
}
Python
def distance_squared(a: Point, b: Point) -> float:
    return (b.x - a.x) ** 2 + (b.y - a.y) ** 2
distanceSquared
time O(1) space O(1)

Every test in the following chapters compares squared distances with squared lengths, and never takes a root.

Comments

No comments yet. Questions and corrections are welcome.

Plain text, line breaks kept. Your IP address is stored only as a one-way hash, to limit spam.