Skip to main content

Collision detection & response

Axially aligned bounding box (AABB)

Closest Point in an AABB

if (x < minX) {
    x = minX;
} else if (x > maxX) {
    x = maxX;
}

if (y < minY) {
    y = minY;
} else if (y > maxY) {
    y = maxY;
}

if (z < minZ) {
    z = minZ;
} else if (z > maxZ) {
    z = maxZ;
}
def cpbbox(
		x,y,
		xmin,ymin,xmax,ymax
	)
	if x<xmin
		x=xmin
	elsif x>=xmax
		x=xmax-1
	end
	if y<ymin
		y=ymin
	elsif y>=ymax
		y=ymax-1
	end	
	return x,y
end

This is done by “pushing” into along each axis in turn. Notice that if the point is already inside the box, this code returns the original point.

Intersection of a Sphere and AABB

def circbbox(
		x,y,r,
		xmin,ymin,xmax,ymax
)
	cx,cy=cpbbox(x,y,xmin,ymin,xmax,ymax)
	(x-cx)**2+(y-cy)**2<=r**2
end

def circbbox2(
		x,y,r,
		bx,by,bw,bh
)
	cx,cy=cpbbox(x,y,bx,by,bx+bw,by+bh)
	(x-cx)**2+(y-cy)**2<=r**2
end

... we first find the point on the box that is closest to the center of the sphere by using the... Closest Point in an AABB...  We compute the distance from this point to the center of the sphere and compare this distance with the radius. (Actually, in practice we compare the distance squared against the radius squared to avoid the square root in the distance computation.) If the distance is smaller than the radius, then the sphere intersects the AABB.

Intersection of Two AABBs

bool aabbsOverlap(const AABB3 &a, const AABB3 &b) {

    // Check for a separating axis.
    if (a.min.x >= b.max.x) return false;
    if (a.max.x <= b.min.x) return false;
    if (a.min.y >= b.max.y) return false;
    if (a.max.y <= b.min.y) return false;
    if (a.min.z >= b.max.z) return false;
    if (a.max.z <= b.min.z) return false;

    // Overlap on all three axes, so their
    // intersection must be non-empty
    return true;
}
def bboxbbox(
		aminx,aminy,amaxx,amaxy,
		bminx,bminy,bmaxx,bmaxy
	)
	not (
		aminx>=bmaxx || amaxx<=bminx ||
		aminy>=bmaxy || amaxy<=bminy
	)
end

def bboxbbox2(
		ax,ay,aw,ah,
		bx,by,bw,bh
	)
	not (
		ax>=bx+bw || ax+aw<=bx ||
		ay>=by+bh || ay+ah<=by
	)
end

Test if left is on the right or right on the left or top below bottom or bottom above top (same front and back for 3D) - not overlapping.

There is a dynamic test described which tells interval of time when the two boxes overlap when the collision starts given velocity vector.

Dynamic intersection of Two AABBs

# Overlap of segmetn with dynamic
# segment moving by v
# Returns [time enters, time leaves]
# time enters:
#		between 0.0,1.0 - s and m will
#			overlap next frame
#  < 0.0 - s is inside m
#  > 1.0 - s will overlap with m in
#		 time enters frames
def seg_dyn(
		smin,smax,
		mmin,mmax,
		v
	)
	te,tl=if v!=0
		te=(smin-mmax).to_f/v
		tl=(smax-mmin).to_f/v
		te,tl=tl,te if te>tl # right to left
		return te,tl
	else
		# Not moving in x axis
		if not (smin>=mmax||smax<=mmin)
			# Always overlaps
			return -Float::INFINITY,Float::INFINITY
		else
			# Never overlaps
			return Float::INFINITY,Float::INFINITY
		end
	end
end

# Stationary with moving bbox
# colision
def bboxbbox_dyn_iv(
		sminx,sminy,smaxx,smaxy,
		mminx,mminy,mmaxx,mmaxy,
		vx,vy
	)
	tex,tlx=seg_dyn(
		sminx,smaxx,
		mminx,mmaxx,
		vx
	)
	tey,tly=seg_dyn(
		sminy,smaxy,
		mminy,mmaxy,
		vy
	)
	te=[tex,tey].max
	tl=[tlx,tly].min
	if te>tl
		# never colide
		return Float::INFINITY,Float::INFINITY
	else
		return te,tl
	end
end

# Returns
#		nil - won't collide next frame
#		[0,1> - will collide next frame
#		<0 - collides
def bboxbbox_dyn(
		sminx,sminy,smaxx,smaxy,
		mminx,mminy,mmaxx,mmaxy,
		vx,vy
	)
	te,tl=bboxbbox_dyn_iv(
		sminx,sminy,smaxx,smaxy,
		mminx,mminy,mmaxx,mmaxy,
		vx,vy
	)
	if te>=0.0&&te<1.0
		# will collid in te to next frame
		te
	elsif te<0.0&&tl>0.0
		# collides
		te
	else
		# won't collid
		nil
	end
end