game physics

Game physics and collision detection — AABB, circles, SAT, and response

📘 gamedev-academy · Part 5 of 15 · Module 2 · Core
A free, step-by-step course on buying, selling & trading domains. View the full series →
Game physics and collision detection — AABB, circles, SAT, and response
Key takeaways
  • AABB collision is four comparisons — it is the workhorse of 2D games and should be your default test.
  • Always resolve collisions by pushing objects apart and zeroing the overlap velocity — skipping resolution causes tunneling.
  • SAT handles any convex polygon but is more expensive — use it only for shapes that cannot be approximated by boxes or circles.

Understanding game physics is the foundation of this guide. Below we break it down step by step so you can act with confidence.

Collision detection and physics response are what make a game world feel real. Without them, objects pass through each other and gravity does not exist.

This lesson covers the collision algorithms you need for 2D games: AABB, circle-circle, circle-AABB, and the Separating Axis Theorem for convex polygons. You will also build a simple physics system with gravity, friction, and restitution.

Game Physics: AABB collision detection

AABB (Axis-Aligned Bounding Box) is the simplest and most common collision test. Two rectangles overlap if and only if they overlap on both axes:

🔬 Demo: AABB intersection test
function aabbCollision(a, b) {
  return a.x < b.x + b.w &&
         a.x + a.w > b.x &&
         a.y < b.y + b.h &&
         a.y + a.h > b.y;
}

// Collision response — push out of overlap
function resolveAABB(a, b) {
  const overlapX = Math.min(a.x + a.w - b.x, b.x + b.w - a.x);
  const overlapY = Math.min(a.y + a.h - b.y, b.y + b.h - a.y);

  if (overlapX < overlapY) {
    // Push on X axis
    a.x += (a.x < b.x) ? -overlapX : overlapX;
    a.vx = 0;
  } else {
    // Push on Y axis
    a.y += (a.y < b.y) ? -overlapY : overlapY;
    if (a.y < b.y) a.grounded = true;
    a.vy = 0;
  }
}

Circle and circle-AABB collision

Circle collision is distance-based. Circle-AABB finds the closest point on the rectangle to the circle center:

🔬 Demo: Circle collision algorithms
function circleCollision(a, b) {
  const dx = a.x - b.x, dy = a.y - b.y;
  const dist = Math.sqrt(dx * dx + dy * dy);
  return dist < a.radius + b.radius;
}

function circleAABB(circle, rect) {
  const cx = Math.max(rect.x, Math.min(circle.x, rect.x + rect.w));
  const cy = Math.max(rect.y, Math.min(circle.y, rect.y + rect.h));
  const dx = circle.x - cx, dy = circle.y - cy;
  return dx * dx + dy * dy < circle.radius * circle.radius;
}

// Elastic bounce between two circles
function resolveCircles(a, b) {
  const dx = b.x - a.x, dy = b.y - a.y;
  const dist = Math.sqrt(dx * dx + dy * dy);
  const nx = dx / dist, ny = dy / dist;
  const relV = (a.vx - b.vx) * nx + (a.vy - b.vy) * ny;
  if (relV > 0) return; // separating
  a.vx -= relV * nx; a.vy -= relV * ny;
  b.vx += relV * nx; b.vy += relV * ny;
}

Gravity, friction, and bounce

A basic physics step applies gravity, then friction, then integrates velocity:

🔬 Demo: 2D physics integration
const GRAVITY = 980;   // px/s^2
const FRICTION = 0.85;
const BOUNCE = 0.6;    // restitution

function physicsStep(entity, dt) {
  // Gravity
  entity.vy += GRAVITY * dt;

  // Integrate
  entity.x += entity.vx * dt;
  entity.y += entity.vy * dt;

  // Floor collision
  if (entity.y + entity.h > FLOOR_Y) {
    entity.y = FLOOR_Y - entity.h;
    entity.vy *= -BOUNCE;
    entity.vx *= FRICTION;
    entity.grounded = true;
    if (Math.abs(entity.vy) < 10) entity.vy = 0;
  }
}

Explore Crypto Trading

Cryptocurrency trading is one of the fastest-growing financial markets — open 24/7, globally accessible, and full of opportunity for those who learn the fundamentals first.

  • Trade spot, futures & copy-trading on one platform
  • Start with as little as $10 — practice risk-free on the testnet
  • Built-in TradingView charts, grid bots & DCA tools

Free Crypto Trading Academy → · Domain Trading Academy →

Separating Axis Theorem for convex polygons

SAT tests whether any axis exists that separates two convex shapes. If no separating axis exists, the shapes collide:

🔬 Demo: SAT implementation
function getAxes(vertices) {
  const axes = [];
  for (let i = 0; i < vertices.length; i++) {
    const a = vertices[i];
    const b = vertices[(i + 1) % vertices.length];
    // Edge normal (perpendicular)
    axes.push({ x: -(b.y - a.y), y: b.x - a.x });
  }
  return axes;
}

function project(vertices, axis) {
  let min = Infinity, max = -Infinity;
  for (const v of vertices) {
    const dot = v.x * axis.x + v.y * axis.y;
    min = Math.min(min, dot);
    max = Math.max(max, dot);
  }
  return { min, max };
}

function satCollision(polyA, polyB) {
  const axes = [...getAxes(polyA), ...getAxes(polyB)];
  for (const axis of axes) {
    const pA = project(polyA, axis);
    const pB = project(polyB, axis);
    if (pA.max < pB.min || pB.max < pA.min) return false;
  }
  return true; // no separating axis found
}

When evaluating game physics, start with the fundamentals: what problem it solves, who it serves, and how it compares to the alternatives buyers consider first.

Start with the fundamentals. Define who you are serving, the single outcome you want, and the budget you are comfortable committing before you compare any options.

A practical rule is to favour clarity over cleverness. The choice that is easiest to understand, spell, remember and act on tends to win over the long run.

Context changes the answer. What is optimal for a fast-moving startup is rarely optimal for an established brand, so weigh your own stage, market and ambitions.

Avoid the common mistakes: rushing the decision, copying competitors blindly, and ignoring the total cost of ownership beyond the very first year of use.

Think in terms of trade-offs rather than a single perfect answer. Every option trades memorability, cost, reach and flexibility in a slightly different mix.

Document your reasoning. Writing down why you chose one path makes it far easier to revisit the decision later and to explain it to partners and investors.

Test with real people. A quick check with a handful of your actual customers will surface problems that no amount of internal debate ever reveals.

Plan for growth. The option that fits today should still make sense when your audience, catalogue or team is several times larger than it is right now.

Finally, revisit the decision periodically. Markets, tools and best practices move quickly, and a choice that was ideal last year may deserve a fresh look today.

When evaluating game physics, start with the fundamentals: what problem it solves, who it serves, and how it compares to the alternatives buyers consider first.

Start with the fundamentals. Define who you are serving, the single outcome you want, and the budget you are comfortable committing before you compare any options.

A practical rule is to favour clarity over cleverness. The choice that is easiest to understand, spell, remember and act on tends to win over the long run.

WSOT 2026: Squad Leader Slots Are Open — Up to $128,000 per Leader

Join Now

Frequently asked questions

When should I use a physics library?

For simple platformers and arcade games, hand-written AABB + circle collision is enough. Use a library like Matter.js or Planck.js when you need joints, constraints, stacking, or complex rigid-body interactions.

How do I handle many collision pairs efficiently?

Use spatial partitioning — a grid, quadtree, or sweep-and-prune. These reduce checks from O(n^2) to near-linear by only testing nearby pairs.

Free Courses — Learn New Skills

No sign-up · No fees · Learn at your own pace

Start Trading

Trade crypto and premium domains — digital assets with real opportunities.

Find your name on Atom

DominantBrand curates the best premium, brandable names from Atom.com — the marketplace with a free AI appraisal, a USPTO trademark check, and secure escrow. Every listing even ships with a designed logo.

Browse Domains