For 90% of 2D beginner games, “did box A touch box B?” is all you need. That’s AABB (axis-aligned bounding box):

local function overlaps(a, b)
  return a.x < b.x + b.w
     and b.x < a.x + a.w
     and a.y < b.y + b.h
     and b.y < a.y + a.h
end

Example: pick up a coin

local player = { x = 100, y = 100, w = 32, h = 32 }
local coin = { x = 300, y = 200, w = 16, h = 16, taken = false }
 
function love.update(dt)
  if not coin.taken and overlaps(player, coin) then
    coin.taken = true
    score = score + 1
  end
end

Example: solid wall (push out)

-- after moving, resolve: push player out along smallest axis
if overlaps(player, wall) then
  -- came from left
  player.x = wall.x - player.w
end

For full platformer resolution (all 4 sides), resolve X first, then Y — two separate checks.

When it breaks

SymptomFix
Tunneling at high speedMove in smaller steps or clamp position on hit; cap dt with dt = math.min(dt, 1/30)
Collision fires every frameResolve position after detection so they no longer overlap.
Nothing collidesprint() both boxes’ x/y/w/h — usually a width/height is 0 or nil