Making a Tile-Based Platformer - Slope Physics (Revised)
My previous article explained one approach to implementing slopes in a tile-based engine. While this worked, it involved adding a lot of slope-specific logic to the engine. This article describes a simpler, more generic approach, that allows tiles of arbitrary shapes.
This approach is used by my open-source 2d-tilebased-engine.
The Many Problems with Slopes
There are a lot of problems with implementing slopes in a tile-based game:
Hitboxes may at times intersect the tile behind or below a slope. Collisions with such tiles must be disabled.
- Regular collisions must still be respected outside the slope, for example, if a slope leads into a wall or a vertical drop.
- It looks strange if a Hitbox is positioned such that only its corner sits on the slope, as the rest of the Hitbox will be floating. Thus, when a Hitbox collides with a slope, it should be positioned such that its horizontal centre sits atop the slope.
- Unlike regular collisions, which stop a Hitbox dead, collisions with ceiling slopes should not slow a falling Hitbox.
- Hitboxes that do not support slope traversal (for example, projectiles) should take into account the angle of the slope when bouncing off the slope.
- By default, fast-moving Hitboxes will fly off the slope, instead of sliding down it.
I won't go into too much detail on the implementation here, but if you're interested you can always check out the source code of my engine. Instead, I will outline the solution in general terms.
The collision algorithm in my engine looks a little like this:
- Move Hitbox in the x-axis, store all collisions in a list
- Move Hitbox in the y-axis, store all collisions in a list
- Move Hitbox to nearest x-/y-collision
Now, where do slopes fit into all this?
Slopes inherently involve both axes. Therefore, they are handled in the post-processing step. In fact, for the purposes of x-/y-collision detection, slopes are treated as completely non-solid.
This post-processing step checks the bounds of the Hitbox to see if it intersects any post-processing tiles. This could include slopes, or any other "special" tiles.
If the Hitbox has collided with a post-processing tile, the physics delegates to the relevant tile class, to perform any final resolution on the Hitbox, and potentially invalidate collisions that have occurred in the x- and y-axes.
In the case of slope tiles, all collisions below or behind the slope are invalidated, and a new vertical collision is registered at the appropriate point on the slope.
The Slope Node
An important concept that my engine relies on is the notion of a slope node. Put simply, this is the point on the Hitbox which collides with slopes. For floor slopes, this is the centre of the bottom edge. For ceiling slopes, it is the centre of the top edge.
If the slope node is in or under a slope tile, that tile is responsible for moving the Hitbox onto the slope. In other words, each slope is responsible for its own region, as shown below.
Considering only a single point on the Hitbox prevents any peculiar cases that may otherwise arise if the Hitbox is intersecting multiple slope tiles at the same time.
The measures described herein take care of a lot of the problems that slopes introduce. The others can be handled similarly, with the help of a little extra logic in our slope tile's post-processing routine.
For example, if the Hitbox intersects a slope tile but the slope node is not inside the solid part of the slope, the slope can check if the Hitbox was grounded in the previous frame. If it was, then the Hitbox should be "pulled" onto the slope. This fixes the problem of fast-moving Hitboxes flying off slopes instead of sliding down them.