collision:
Point to Circle


  Point to Circle collision is detected by checking if the distance between the point and the center of the circle is equal to or less than the circle's radius.


 

No Collision

 

PICO-8 Collision Function

function point_circle_collision( p, c )
    local dx,dy = p.x-c.x, p.y-c.y
    return dx*dx + dy*dy <= c.r*c.r
end
 

Preparing Coordinates

First, we need an easy way to store the coordinates of both a point and a circle as well as the circle's size. We can do that with 2 tables that store separate X, Y, variables. For the size of the circle, we will save a radius r, which is the distance from the center of the circle to the outer edge. Those tables (named p for the point and c for the circle) are what the above function expects.

They could be created like this:

--create object tables
p = { x=10, y=20 }
c = { x=63, y=63, r=30 }

Now we have two points at their own coordinates of X and Y, and the second point will be the center of a circle with a size of r. Next we need to know how to get the values out of the tables. For example, to get the radius of the circle, we use c.r (See Table Shorthand).

--get values out of object table
print( c.r )   --prints 30


Understanding the Math

The good news is you don't need to understand the details of how it works in order to use this collision function in your game. As long as you understand what you need to pass to the function and what you can expect to get returned, then you can simply trust that the math inside is working properly.

However, if you want to take more control over your code and customize how your game collision works, then understanding exactly how and why the math works the way it does is important. So we will break it down as best we can.


Overall, we want to get the distance between the point and the circle by using the coordinates of the point and the center point of the circle. We'll use some algebra and geometry here to figure out the distance. First we can easily find the difference between each point on each axis. Think of the center point of the circle at coordinates ( x1, y1 ) and the point at ( x2, y2 ).

x2 - x1 = horizontal difference

y2 - y1 = vertical difference     


We can do this with any 2 points, imagining them as part of a right triangle, and with just subtraction we get the lengths of 2 sides of that triangle. The 3rd side is the actual distance we want to figure out. Now that we have the lengths of 2 sides of a triangle, we can figure out the third side using the Pythagorean Theorem:

c2 = a2 + b2

Basically it's just a rule about right triangles where the longest side equals the two shorter sides added together, but only when all three sides are multiplied by themselves ("squared").

We can expand that out to look like this:

c×c = a×a + b×b

And this is how we would write it in code:

c*c = a*a + b*b


Applying to our Point and Circle

Let's not get confused when we change from the math where we use A B C for the triangle sides above, back to code where we are using P and C for the point and the circle again. This is how we do the same but with our point and circle data.

  math code
Difference X = x2 - x1 p.x - c.x
Difference Y = y2 - y1 p.y - c.y

In code, we can do that math to get the length of the two smaller sides, and save them in variables named dx and dy.

--difference of points
dx = p.x - c.x
dy = p.y - c.y

The next step is to multiply those sides by themselves, remember:

c*c = a*a + b*b

In the case of Point and Circle, the A is DX and the B is DY, so we want to do:

distance_squared = dx*dx + dy*dy

And now we could simply square root the result to find the distance:

distance = sqrt( distance_squared )

(PICO-8 has a built in math function for this: sqrt)


After we get the distance between the two points, we just need to compare that with the radius of the circle. If the distance is shorter than the radius, then the point must be inside the circle!

Now that you understand the math of finding the distance and comparing it with the radius, play with the demo at the top of this page to see all the pieces come together.



Understanding an Expanded Version

We can combine all of the steps above into a single function, written out clearly to show each step:

function point_circle_collision( p, c )
  --get lengths of two sides; the differences
	local dx = p.x-c.x
	local dy = p.y-c.y

	--get length of 3rd side; the distance
	local distance_squared = dx*dx + dy*dy
	local distance = sqrt( distance_squared )

	--check if point is inside circle radius
	if distance <= c.r then
		return true
	else
		return false
	end
end

This function has parameters (p,c) and it is expecting those to be objects with X, and Y keys, with the circle also having an R key for radius. 

Step 1: get the differences between the points on each axis, which creates an imaginary right triangle.

Step 2: get the distance of the third side of the triangle, the actual distance of the two points.

Step 3: compare the distance and the circle's radius

Step 4: return true if the distance is shorter than the radius or false if it is longer.


You can call this function in two ways to catch the returned true or false result:

--catch result in variable
is_colliding = point_circle_collision( p, c )

if is_colliding then
	print( "collided!" )
end

--directly inside of if statement
if point_circle_collision( p, c ) then
	print( "collided!" )
end

Note that these examples use the same argument names and parameter names just for the convenience of this tutorial, but to make the difference clear, this is how the parameters of the function still uses (p,c) but the arguments when calling the function could be any object table such as:

bullet = { x=10, y=20 }
shield = { x=30, y=40, r=8 }

if point_circle_collision( bullet, shield ) then
	player_health = 0
end

(See more examples below in When to Use this?)


Understanding the Simplified Version

We can simplify and condense the expanded version down to just two lines:

function point_circle_collision( p, c )
    local dx,dy = p.x-c.x, p.y-c.y
    return dx*dx + dy*dy <= c.r*c.r
end

 

This works the exact same way as the expanded version because all the steps of getting the distance and comparing it to the radius is happening here too. 

The first way we condensed the code is what is called multiple assignments. Where we assign more than one value to more than one variable on a single line. That way we assign dx and dy together on the first line of the function, separated by commas.

local dx,dy = p.x-c.x, p.y-c.y

The second trick we do to condense the code is to skip square rooting the distance. We do the math for finding the distance squared and immediately compare it with the circle's radius squared.

dx*dx + dy*dy <= c.r*c.r

 The final trick to condense it is to simply return the result of the math and the comparison, which lets us do all of that in a single line of code. It will do the math first, then compare those results, then return the comparison's result of true or false.

return dx*dx + dy*dy <= c.r*c.r




Point to Circle Collision is not used that often because it is pretty specific. You need a circular game object and something small enough that you only care about a single point.

A classic game that immediately comes to mind is the circular character of Pacman who eats single pixel pellets.

--init
pacman = { x=10, y=20, r=5 }
pellets = {} --empty table
add_pellets() --fills table with points

--update
for pellet in all( pellets ) do
	--collision check
	if point_circle_collision( pellet, pacman ) then
		score += 100
		del(pellet) --remove from game
	end
end

Sometimes you can simplify a larger sprite down to a single point near its center. Many bullet-hell shmups will do this where the ship looks big but the hitbox is only the tiny cockpit. So the cockpit could be the point and enemy bullets could be large circles.

Or the opposite, where the player has a circular energy shield, and the enemy bullets are just points.

--init
ship = { x=10, y=20, r=5 }
shield = true
enemy_bullets = {} --empty table
add_enemy_bullets() --fills table with points

--update
for bullet in all( enemy_bullets ) do
	--collision check
	if shield then
		if point_circle_collision( bullet, ship ) then
			del(bullet) --remove from game
		end
	else
		if point_point_collision( bullet, ship ) then
			health -= 10
			del(bullet) --remove from game
		end
	end
end

Multiple Collision Check Example

This shows how you could use two different collision functions. If the shield is on, then it checks point_circle_collision(), but if the shield is off, then it checks point_point_collision() to see if the bullet hits the player cockpit instead and the player could lose health.


Another example of using Point to Circle Collision is when your game has a circular field of view and you want to determine if the player or other objects are within that space. That could be for creating fog-of-war in a strategy game, a camera's line-of-sight for a stealth game, or the light radius of a torch in an adventure game.




35

9 Apr 2025

Font