Tile Collision in Gamemaker Studio 2

Brenan Wayland
Oct 6, 2018 · 4 min read

Many GMS2 tutorials will show you how to make your character collide with the map by using invisible objects, taking advantage of the engine’s built-in object collision functions. And while this is a quick and simple technique, it can become unsustainable and tedious with larger rooms and projects, especially if your game has a lot of obstacles like walls, trees, rocks, etc. The object count can add up quickly.

There are certainly ways to optimize object-based collision — merging objects at run-time, for example — but I’ve found that leveraging tilemaps to handle collision (and potentially more than that) is a more elegant and intuitive solution. For one, it’s much easier to paint tiles into a room than it is to place collision objects. But the primary reason is that it’s faster and doesn’t require as much processing, with the downside of losing access to built-in functions to help you. Instead, you’ll have to write your own script(s) to handle this, but it’s simpler than you might think!

Below is my implementation of tile collision. Let’s look at the code first, and then talk about what’s going on.

var xx, yy, tm, x0, y0, col, xPoints, yPoints, i, j;xx = argument0;
yy = argument1;
//save current position
x0 = x;
y0 = y;
//retrieve the collision tile layer's ID
//replace "Collision" with the name of the layer containing collision tiles
tm = layer_tilemap_get_id(layer_get_id("Collision"));
//(temporarily) move to the position we want to check
x = xx;
y = yy;
//initialize arrays containing all of the points along each axis that you want to check
xPoints = [x,bbox_right,bbox_left];
yPoints = [y,bbox_top,bbox_bottom];
//initialize the return variable to false
col = false;
//iterate through all of the possible points of contact and check if there is a tile at that pixel
for (i = 0; i < array_length_1d(xPoints); i++) {
for (j = 0; j < array_length_1d(yPoints); j++) {
col = tilemap_get_at_pixel(tm,xPoints[i],yPoints[j]);
if (col) {
break;
}
}
if (col) {
break;
}
}
//return to the original position
x = x0;
y = y0;
return col;

I put this code in a script called tileCollision() so I can easily call it anywhere tile collision is necessary. The idea behind it is simple — identify several points on the character that might come in contact with a collision tile. Separate the x- and y-values into arrays so that we can use iteration and a nested loop to check every possible combination of those values, and thus every (x,y) point that we want to check. Check if there is a collision tile at each point. If there is, then we can break out of the loop (we’ve got to break twice since it’s a nested loop).

Besides saving us from typing tilemap_get_at_pixel over and over, using arrays for this also makes it easier to add new points to check later if you end up needing them.

For example, in a 4-directional, Legend of Zelda-type game I’ve been working on recently, the sizes of the character and the trees made it so that there were certain points where none of the points of contact actually touched the collision tiles over the trees, allowing the character to “slip” through even though it seemed like he should have been halted. While the proper solution is to rectify the size of my sprites, I was able to easily combat this problem instead by amending the tileCollision() script like so:

xPoints = [x,bbox_right,bbox_left,(bbox_right+x)/2,(bbox_left+x)/2];
yPoints = [y,bbox_top,bbox_bottom,(bbox_top+y)/2,(bbox_bottom+y/2)];

By adding these extra checks halfway between the original points, I was able to ensure that there were no “cracks” that the character could slip through, although this is at the cost of adding a little extra run-time complexity.

Additional Thoughts

It can definitely be frustrating at first to leave behind the ease of GMS2’s object collision and built-in functions, and likewise it can be tedious to perfect your tile collision implementation, but overall it’s more efficient and, in a lot of ways, easier. However, through lots of trial and error I’ve become aware of several pitfalls in tile collision that you should be mindful of as you’re making your game.

The biggest thorn is sprite and tile sizes. While you don’t have to make your collision tiles the same size as your actual map tiles (making them smaller gives you more precise control over the collision mask of the map), it’s good practice, and makes the most sense. With that in mind, the sprites of your characters should correspond to the size of the tiles so that the corners of its bounding box will always come in contact with the collision tiles when approaching them. Otherwise, characters will slide through walls, get stuck in platforms, etc. It’s possible to fix this by checking additional points, like I mentioned above, but this can be costly, particularly if there are a lot of characters in your game that could be colliding with tiles.

Tile collision also interferes with some other GMS2 functionality, like motion planning. Since motion planning creates paths by checking for object collision, your AI characters won’t be able to navigate a map that uses tile collision. Once again, you’d probably have to come up with a custom solution.

I hope you find this article informative, and as always, feel free to contact me if I made any mistakes, I could do something better, if you have any questions, or if you just want to chat about games.

If you’d like to see more, check out my YouTube channel for video tutorials, dev logs, and more, and follow me on Twitter!

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade