Making a Tile-Based Platformer - Slope Physics
Slopes are one of the absolute hardest things to implement in a tilebased engine, and they inherently require some special processing if they are to fit in with our previous notion of all tiles being either "solid" or "not solid". Undoubtedly there are many approaches to this problem, but I will describe the one I have found to be the easiest to understand, and the most self-contained in terms of code.
Slope Post-Processing
We will define slopes as being non-solid. According to our collision algorithm, then (discussed in the previous article), the player can walk right through them. However, after our collision handling takes place, we will perform some special "slope post-processing" to ensure that our player ends up on the slope rather than inside it.
In fact, I recommend disabling collision handling in the x-axis entirely when the player is on a slope, to prevent the player colliding with the tiles "between" slopes (shown below), or at the tops of slopes. When the player leaves the slope, collisions in the x-axis can be re-enabled.
The Stair Dilemma
Another complication in handling slopes is that, unless the player is absurdly slow or gravity absurdly strong, when the player is going down a slope, he tends to bounce as though he is going down stairs. The reason for this is that his x-speed carries him off the slope, and it takes a few frames before gravity pulls him back onto it.
To solve this, we need to establish when a player is on a slope, and set some "slope flag". In subsequent frames, if the player is not on a slope but this flag is set, we know that the player should be on a slope, and we can therefore pull him down onto it.
We also need to know when the player is no longer on slope, so that we can clear this slope flag.
The Problem, Simplified
The problem, then, is as follows:
- How do we check if the player is intersecting a slope?
- How do we put him on top of a slope?
- How do we check when he is no longer intersecting a slope?
How do we check if the player is intersecting a slope?
This is actually the hardest step. We need to determine which slope the player is, or should be, intersecting. Here is the algorithm in simple English:
Check the tile at each corner of the player. For each corner:
- If it's a slope tile, then chances are we are intersecting a slope. But not so fast! We still need to check that this corner of the player is inside the solid part of the slope.
- If we always claim to be intersecting a slope whenever we enter a slope tile, then the player will suddenly snap to the slope when it's not appropriate, for instance when jumping onto the slope. When the player is in the non-solid part of the slope tile, he should still move naturally until he actually reaches the slope.
- If the player's slope flag is set, we know we are supposed to be on a slope so can omit this extra check.
Player not intersecting slope |
Player intersecting slope |
- If it's not a slope but instead a solid tile, it could be that the player is intersecting a tile that's "in between" slopes, as described previously. Just in case, we'll need to check a couple more tiles. Which tiles to check depends on the type of slope, but if it's a right slope (as shown), you should check the tile above and the tile to the left.
- If both of these tiles are slopes, then we are "in between" slopes. In this case, we should behave as though we are intersecting whichever slope tile is vertically in line with this corner of the player.
Phew!
If, after all this, we find that the player is not intersecting a slope, but the slope flag is set, we may be experiencing the "stair dilemma" explained above. In this case, the tile below the player is probably a slope, and we should take that to be the slope we are intersecting.
How do we put him on top of a slope?
Once we know which slope tile the player is (or should be) intersecting, this step is easy. We are only going to be changing the player's y-position, which, fortunately, is entirely dependent on his x-position. An example function, in pseudo-code, for a right slope:
putPlayerOnSlope(player, slopeLeft, slopeTop){ distIntoTileX = player.right - slopeLeft; distIntoTileY = player.bottom - slopeBottom; slope_y_at_x = Tile.HEIGHT - distIntoTileX; dy = slope_y_at_x - distIntoTileX; player.moveY(dy); }
How do we check when he is no longer intersecting a slope?
When any of the following occur, the player should no longer be considered "on a slope":
- The player has reached the top of the slope.
- This involves adding an extra case to the end of our "is the player intersecting a slope" algorithm. Specifically, if a right slope is on the left of the player, it's because he's reached the top. The same is true when a left slope in on the right of the player.
- The player has reached the bottom of the slope.
- If the player's slope flag is set, I said that the tile below him is probably a slope tile. Well, if it's not, then guess what? We've reached the bottom!
- The player has jumped.
- This should be easy to detect since it's based on a keypress.
Visual Adjustment
Notice that in the images above, only the corner of the player is touching the slope, which can make it look as though the player is floating. The problem only gets worse with larger entities.
To remedy this, instead of considering the corners of the player, we can just consider the middle of his top and bottom edges. Simple!
Closing Remarks
Well, congratulations if you made it this far through the article. I hope it made sense, and I hope it was helpful or at least interesting. These notes are the result of many hours of very frustrating research and experimentation, and even after all of this there are still some odd corner cases that require additional work.
For example, if you have a 1-tile pit at the bottom of a slope - well, you've disabled x-collisions, so if you come down the slope fast enough you'll go right through the wall of the pit.
The way slopes are implemented in this article also places certain restrictions on their usage; if you want two slopes next to each other (like a peak), I'm fairly sure that will break something.
The point I'm trying to make is that slopes are complicated - but they are possible! Yes, there are complications, but once they are understood they are easily solvable. The method described in this article can be easily extended to handle extra cases. I think even slopes of different gradients (as opposed to just the 45-degree slopes described here) shouldn't be too hard, but I haven't got that far yet myself!
Useful Links
Previous: Making a Tile-Based Platformer - Physics
Published 04/10/2014