POLYGON/POINT
Circle and rectangle collisions are great, and oftentimes simplifying the collision of complex shapes using bounding boxes and circles makes sense. But there are applications when we want more accuracy. Fortunately, most of the remaining examples use ideas we've already covered, even if how we apply them gets more complicated.
In this first example, we'll check if a point is inside a complex polygon. We define our polygon using a set of X/Y points called vertices. To store these points, we'll use an array of Vector
objects. Vectors simply store X/Y (or X/Y/Z) coordinates. This makes storing points a little easier, and Processing gives us some fancy math for Vectors that would be tricky otherwise. If you haven't used Vectors before, read at least the first part of this tutorial on the Processing site before continuing.
First, we create an array of four Vectors, one for each corner of our polygon:
var vertices = ;
Then, we set their X/Y positions. Here we're drawing a distorted trapezoid (like above), but you could make more complicated shapes, or even randomize the points!
vertices0 = ; // set X/Y positionvertices1 = ;vertices2 = ;vertices3 = ;
To check for collision, we're going to use a separate boolean variable. This will be inside our function later, so if this gets confusing, jump to the full example at the bottom.
var collision = false;
Then we need to go through the vertices one-by-one. To do this we use a for loop with the variable current
. But also want the next vertex in the list so we can form a line (a side of the polygon). To do this, we use a second variable called next
. Here's what the loop looks like:
var next = 0;for var current = 0; current < verticeslength; current++ // get next vertex in list // if we've hit the end, wrap around to 0 next = current + 1; if next == verticeslength next = 0;
Then we can use current
and next
to retrieve the Vectors from our array:
var vc = verticescurrent; // c for "current"var vn = verticesnext; // n for "next"
Now for the if statement. We can access the X/Y coordinates of each vertex using the syntax vc.x
and vc.y
. This statement is pretty tricky, so here's the whole thing, then we'll break it down into its parts:
if vcy > py && vny < py || vcy < py && vny > py && px < vnx - vcx * py - vcy / vny - vcy + vcx collision = !collision;
There are two tests happening here. The first checks if the point is between the two vertices in the Y direction:
vcy > py && vny < py || vcy < py && vny > py
We test if the point is either above vc.y
and below vn.y
, or below vc.y
and above vn.y
. Here's what this looks like visually:
Note: There's a fancier, more concise way of writing this if statement, if you prefer:
vcy > py != vny > py
That's a little confusing: it does the same test, but only evaluates true
if both tests are not the same as each other!
Next up is a more complicated test. This is based on the Jordan Curve Theorem, which is pretty intense math so we'll skip explaining it. (If you understand how this algorithm works, please do let me know!)
px < vnx-vcx * py-vcy / vny-vcy + vcx
If both checks are true, we switch collision
to its opposite value. This is different than our previous tests, where we set the collision to simply true
or false
. After we've gone through all the vertices, whatever the final state of collision
is is the result.
// set collision to the opposite of its current statecollision = !collision;
Here's a full example with everything together:
var px = 0; // point positionvar py = 0; // array of PVectors, one for each vertex in the polygonvar vertices = ; { var canvas = ; canvasparent"sketch"; ; ; // make the point easier to see // set position of the vertices // here we draw a distorted trapezoid, but // you could make much more complex shapes // or even randomize the points! vertices0 = ; vertices1 = ; vertices2 = ; vertices3 = ;} { ; // update point to mouse coordinates px = mouseX; py = mouseY; // check for collision // if hit, change fill color var hit = ; if hit ; else ; // draw the polygon using beginShape() ; ; for var i = 0; i < verticeslength; i++ ; ; // draw the point ; ;} // POLYGON/POINT { var collision = false; // go through each of the vertices, plus // the next vertex in the list var next = 0; for var current = 0; current < verticeslength; current++ // get next vertex in list // if we've hit the end, wrap around to 0 next = current + 1; if next == verticeslength next = 0; // get the PVectors at our current position // this makes our if statement a little cleaner var vc = verticescurrent; // c for "current" var vn = verticesnext; // n for "next" // compare position, flip 'collision' variable // back and forth if vcy > py && vny < py || vcy < py && vny > py && px < vnx - vcx * py - vcy / vny - vcy + vcx collision = !collision; return collision;}
This function is designed to take any number of vertices, so it can handle very complex shapes! However, the more vertices you check, the slower the function will be. If you wanted to do this in a full game, even just a few of these tests on complex shapes could slow your game to a crawl. Balance the need for accuracy with speed: whatever feels intuitive is probably the right way to go.
This example is based on this answer by nirg and Pranav from StackOverflow.