Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From \$16.50/m

A* Pathfinding for 2D Grid-Based Platformers: Different Character Sizes

Difficulty:IntermediateLength:MediumLanguages:

In this tutorial, we'll extend our grid-based platformer pathfinder so that it can cope with characters that take up more than one cell of the grid.

If you haven't added one-way platform support to your code yet, I recommend you do so, but it's not necessary in order to follow this tutorial.

Demo

You can play the Unity demo, or the WebGL version (100MB+), to see the final result in action. Use WASD to move the character, left-click on a spot to find a path you can follow to get there, right-click a cell to toggle the ground at that point, and click-and-drag the sliders to change their values.

Character Position

The pathfinder accepts the position, width, and height of the character as an input. While width and height are easy to interpret, we need to to clarify which block the position coordinates refer to.

The position that we pass needs to be in terms of map coordinates, which means that, yet again, we need to embrace some inaccuracy. I decided that it would be sensible to make the position refer to the bottom-left character tile, since this matches the map coordinate system.

With that cleared up, we can update the pathfinder.

Checking That the Goal is Realistic

First, we must make sure that our custom-sized character can fit in the destination location. Until this point, we've only checked one block to do this, as that was the maximum (and only) size of the character:

Now, however, we need to iterate through every cell that the character would occupy if it were standing in the end position, and check whether any of them are a solid block. If they are, then of course the character cannot stand there, so the goal cannot be reached.

To do this, let's first declare a Boolean which we will set to false if the character is in a solid tile and true otherwise:

Next, we'll iterate through every block of the character:

Inside this loop, we need to check whether a particular block is solid; if so, we set inSolidTile to true, and exit the loop:

But this is not enough. Consider the following situation:

If we were to move the character so that its bottom-left block occupied the goal, then the bottom-right block would be stuck in a solid block—so the algorithm would think that, since the character doesn't fit the goal position, it is impossible to reach the end point. Of course, that's not true; we don't care which part of the character reaches the goal.

To solve this problem, we will move the end point to the left, step by step, up to the point where the original goal location would match the bottom-right character block:

Note that we shouldn't simply check the bottom left and right corners, because the following case may occur:

Here, you can see that if either of the bottom corners occupy the goal location, then the character would still be in solid ground on the other side. In this case, we need to match the bottom-center block with the goal.

Finally, if we can't find any place where the character would fit, we might as well exit the algorithm early:

Determining the Starting Position

To see whether our character is on the ground, we need to check whether any of the character's bottom-most cells are directly above a solid tile.

Let's look at the code we used for a 1x1 character:

We determine whether the starting point is on the ground by checking whether the tile immediately below the starting point is a ground tile. To update the code, we'll simply make it check below all of the bottom-most blocks of the character.

First, let's declare a Boolean that will tell us whether the character starts on the ground. Initially, we assume that it doesn't:

Next, we'll iterate through all the bottom-most character blocks and check whether any of them are directly above a ground tile. If so, then we set startsOnGround to true and exit the loop:

Finally, we set the jump value depending on whether the character started on the ground:

Checking the Successor's Bounds

We need to change our successor's bounds check as well, but here we don't need to check every tile. It's good enough to check the contour of the character—the blocks around the edge—because we know that the parent's position was fine.

Let's look how we checked the successor's bounds previously:

We'll update this by checking whether any of the contour blocks are within a solid block. If any of them does, then the character cannot fit in the position and the successor should be skipped.

Checking the Top and Bottom Blocks

First, let's iterate over all the top-most and bottom-most blocks of the character, and check whether they overlap a solid tile on our grid:

The CHILDREN_LOOP_END label leads to the end of the successor loop; by using it, we skip the need to first break out of the loop and then continue to the next successor in the successor loop.

When a Tile in the Air Can Be Considered "OnGround"

If any of the bottom blocks are right above a solid tile, then the successor must be on the ground. This means that, even if there's no solid tile directly under the successor cell itself, the successor will still be considered an OnGround node, if the character is wide enough.

Checking Whether the Character is at the Ceiling

If any of the tiles above the character are solid, then the character is at the ceiling.

Checking the Blocks at the Sides of the Character

Now we just need to check that there aren't any solid blocks in the left and right cells of the character. If there are, then we can safely skip the successor, because our character won't fit that particular position:

Conclusion

We've removed a fairly significant restriction from the algorithm; now, you have much more freedom in terms of the size of your game's characters.

In the next tutorial in the series, we'll use our pathfinding algorithm to power a bot that can follow the path itself; just click on a location and it'll run and jump to get there. This is very useful for NPCs!