Monday, November 17, 2014

Yet Another Addendum: 2D Platformer Collision Detection in Unity

This is yet another addendum to my previous addendum to my original article on creating 2D platformer collision detection in Unity using raycasting. There was a last remaining bug in the system in which the player would get snagged on a corner tile, effectively getting stuck until they jumped or moved in the opposite direction. This is still a huge improvement over just falling through the tile completely (which was the case originally), but still annoying and needed addressing.

You can see the bug in the video below. Just fast forward to the 1:07 mark and watch the Angel in the lower right corner.



It actually took me a long time to come up with a fix for this, even though now it's such a simple and obvious fix. Without reiterating all of the details of the collision system, I essentially cast rays towards the directions the player is moving. If the player is moving left, I cast evenly spaced horizontal rays from the box collider (4 in this game's case). To cover the corner of the box collider, I use a margin variable to cast ever so slightly out of the box collider bounds; refer to my previous posts on this for further detail.

This was causing the snagging issue because while the player's box collider wasn't actually colliding the tile (and it doesn't really appear to be either in the game), a collision was still being detected due to the margin. By itself, this isn't too bad, but we'd also get a y-axis collision detected, and this, along with a x-axis collision, would cause the snagging/jittering effect.

The ideal solution is to reduce the margin of the raycasts so that we're not casting outside of the box collider, and still guarding against corner collisions. Thus, we introduce diagonal raycasts from the corners of the box collider!

Here is the new code in all its glory.
Note the Move method and when we perform the diagonal raycasts. We only want to perform them when the player is moving through the air, by checking that the player is moving in both x and y axes, not in a a side collision nor on the ground. We then perform a simple raycast (always with the origin in the center of the collider) in the direction the player is moving. When a corner is hit, we simply stop the x-axis movement.

A simple solution to a problem that was haunting me for a while.