# Reacting to rectangle on rectangle collisions

I don't know how to react to collisions between two axis aligned rectangles that have x, y, width and height values (x and y are from the centre of the box) to make them simply not overlap.

I figured I'd just make them move away from each other depending on how far they intersect in the opposite direction (left, right, up or down) of where they collided. If I check for collisions only on the x axis or only on the y axis it works fine, but when checking for both collisions crazy stuff happens.

This code executes when the first box collides with the second. It's in lua but feel free to answer in anything that isn't to too counter-intuitive.

if box1.x < box2.x then
box1.x = box2.x - (box1.width / 2) - (box2.width / 2)
end

if box1.x > box2.x then
box1.x = box1.x - (box1.x - box2.x - (box1.width / 2) - (box2.width / 2))
end

if box1.y < box2.y then
box1.y = box2.y - (box1.height / 2) - (box2.height / 2)
end

if box1.y > box2.y then
box1.y = box1.y - (box1.y - box2.y - (box1.height / 2) - (box2.height / 2))
end


The thing is that the boxes only actually overlap if they overlap on both axes. You can therefore compute the overlap on both axes, then decide whether to move the boxes.

First compute how far apart the box centres are:

local dx = box2.x - box1.x
local dy = box2.y - box1.y


Then compute how far apart they should be at least (in absolute value):

local dw = (box2.width + box1.width) / 2
local dh = (box2.height + box1.height) / 2


Now a bit of maths tells us the excess in both directions:

local movex = math.max(dw - math.max(dx, 0), 0) - math.max(dw + math.min(dx, 0), 0)
local movey = math.max(dh - math.max(dy, 0), 0) - math.max(dh + math.min(dy, 0), 0)


And you can finally move the boxes away from each other if needed, either in the X or in the Y direction. If both movex and movey are zero then the boxes don't move:

if math.abs(movex) <= math.abs(movey) then
box1.x -= movex / 2
box2.x += movex - movex / 2
else
box1.y -= movey / 2
box2.y += movey - movey / 2
end