# A* Pathfinding for 2D Grid-Based Platformers: Adding One-Way Platforms

In this short tutorial, we'll extend our platformer pathfinder so that it can deal with one-way platforms: blocks that the character can jump through and also step on. (Technically, these are two-way platforms, since you can jump through them from either direction, but let's not split hairs!)

## 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 middle-click to place a one-way platform.

## Changing the Map to Accommodate One-Way Platforms

To handle the one-way platforms, we need to add a new tile type to the map:

 1 public enum TileType  2 {  3  Empty,  4  Block,  5  OneWay  6 } 

One-way platforms have the same pathfinder weight as empty tiles—that is, 1. That's because the player can always go through them when jumping up; they only stop him when he's falling, and that in no way impairs the character's movement.

We also need a function that lets us know if the tile at a given position is specifically a one-way platform:

 1 public bool IsOneWayPlatform(int x, int y)  2 {  3  if (x < 0 || x >= mWidth  4  || y < 0 || y >= mHeight)  5  return false;  6 7  return (tiles[x, y] == TileType.OneWay);  8 } 

Finally, we need to change Map.IsGround to return true if a tile is either a solid block or a one way platform:

 1 public bool IsGround(int x, int y)  2 {  3  if (x < 0 || x >= mWidth  4  || y < 0 || y >= mHeight)  5  return false;  6 7  return (tiles[x, y] == TileType.OneWay || tiles[x, y] == TileType.Block);  8 } 

That's the map part of the code sorted; now we can work on the pathfinder itself.

## Adding New Node Filtering Conditions

We also need to add two new node filtering conditions to our list. Remember, our list currently looks like this:

1. It is the start node.
2. It is the end node.
3. It is a jump node.
4. It is a first in-air node in a side jump (a node with jump value equal to 3).
5. It is the landing node (a node that had a non-zeo jump value becomes 0).
6. It is the high point of the jump (the node between moving upwards and and falling downwards).
7. It is a node that goes around an obstacle.

We want to add these two conditions:

• The node is on a one-way platform.
• The node is on the ground and the previous node was on a one-way platform (or vice-versa).

### Including Nodes That Are One-Way Platforms

The first point: we always want to include a node if it's on a one-way platform:

 1 if ((mClose.Count == 0)  2  || (mMap.IsOneWayPlatform(fNode.x, fNode.y - 1))  3  ...  4  mClose.Add(fNode); 

### Include Ground Nodes if Previous Node Was a One-Way Platform

The second point: we need to include a node if it is on the ground and the previous node is on a one-way platform:

 1 if ((mClose.Count == 0)  2  || (mMap.IsOneWayPlatform(fNode.x, fNode.y - 1))  3  || (mGrid[fNode.x, fNode.y - 1] == 0 && mMap.IsOneWayPlatform(fPrevNode.x, fPrevNode.y - 1))  4  ...  5  mClose.Add(fNode); 

### All Together, Then...

Here's a refreshed list of node filter conditions; the algorithm will let through any node that fulfils any of the following requirements:

1. It is the start node.
2. It is the end node.
3. The node is on a one-way platform.
4. The node is on the ground and the previous node was on a one-way platform (or vice-versa).
5. It is a jump node.
6. It is a first in-air node in a side jump (a node with jump value equal to 3).
7. It is the landing node (a node that had a non-zero jump value becomes 0).
8. It is the high point of the jump (the node between moving upwards and and falling downwards).
9. It is a node that goes around an obstacle.

And here's the code checking all these conditions:

 1 if ((mClose.Count == 0)  2  || (mMap.IsOneWayPlatform(fNode.x, fNode.y - 1))  3  || (mGrid[fNode.x, fNode.y - 1] == 0 && mMap.IsOneWayPlatform(fPrevNode.x, fPrevNode.y - 1))  4  || (fNodeTmp.JumpLength == 3)  5  || (fNextNodeTmp.JumpLength != 0 && fNodeTmp.JumpLength == 0) //mark jumps starts  6  || (fNodeTmp.JumpLength == 0 && fPrevNodeTmp.JumpLength != 0) //mark landings  7  || (fNode.y > mClose[mClose.Count - 1].y && fNode.y > fNodeTmp.PY)  8  || (fNode.y < mClose[mClose.Count - 1].y && fNode.y < fNodeTmp.PY)  9  || ((mMap.IsGround(fNode.x - 1, fNode.y) || mMap.IsGround(fNode.x + 1, fNode.y))  10  && fNode.y != mClose[mClose.Count - 1].y && fNode.x != mClose[mClose.Count - 1].x))  11  mClose.Add(fNode); 

## How Filtering Looks With One-Way Platforms

Finally, here's an example of filtering with one-way platforms.

## Conclusion

That's all there is to it! It's a simple addition, really. In the next tutorial in this series, we'll add a slightly more complicated (but still fairly straightforward) extension, allowing the pathfinding algorithm to deal with characters that are larger than 1x1 blocks.