Monday, June 1, 2015

Collision with tile corners/seams in 2D platformer


There seems to be a lot of somewhat related questions answered already, but none I read answered my exact problem.


I have a 2D platformer whose world is made entirely out of tiles that are in a regular grid. When there is a collision, I just move the character to the position he was in prior to the collision and it works well enough for me.


The character can fly/slide in the world, so I also want to set the speed towards the collided tile to zero. Here lies my problem.


At the moment the only way to do this I can think of is to check the difference between the character position and the center of the tile. If (the absolute value of the) x-difference is greater, then it is a horizontal collision. If y-difference is greater, then it's a vertical one. However, as the character is sliding horizontally on tiles, from one tile to another, sometimes he stops at the seam of the tiles because momentarily the x-difference becomes a tiny bit greater than the y-difference (due to float inaccuracies I suspect). I don't know if it's possible to change this algorithm to fix this, or if I need a new one. Any algorithm will do, as long as it fulfills the "requirements" stated above.


If at all possible, I want to treat the x- and y-collisions identically, because the movement is not just walking horizontally.


Image illustration: http://i.stack.imgur.com/URkoV.png




Answer



This is a really common problem, and I'd be very surprised if it hasn't been answered several times already.


Short version of several potential fixes:


1) Ignore internal edges between tiles using flags. For each tile, set a flag for each of its four sides indicating whether that side is external (adjacent to an empty cell) or not. Then only do collision tests against edges that have the flag set. So in your example, since the edge between the two floor tiles is an internal edge, you would not perform the x axis test against that edge. There is a great series of articles by the N+ author on metanet that takes this technique a bit further, with working Flash demos.


2) Collapse external edges into a simplified set of lines. This is a bit more complicated that the previous method, but can be more flexible. Essentially, find all the external edges and convert them into line segments. The find all line segments that share a vertex and have the same normal an merge them into a single line segment. Then switch your collision detection to use AABB to line segment tests. The result is that you won't have any internal edges to test against in the collision data structure. Also, this makes it easier to add arbitrarily shaped tiles later.


3) A simple but error prone method is to just special case a lot of your collisions logic. This is what some old school classic platformers did. It's often sufficient to take the direction of movement and then do collision detection against the "most perpendicular" of the x or y axis, then do the other. For instance, if the player is mostly moving horizontally, first do collision along the y axis and resolve, then against the x axis. Include a small bit of tolerance. Result will be that the player is pushed out of the floor enough that the problematic internal edge collision falls within the error tolerance and is ignored.


4) use hot spot collision detection. This is what most old school platformers did. I don't care for this method myself and it takes some work to explain (although it's very simple to implement), but Google can likely find a few articles on it for you. You may find this method best suits your needs if youre aiming for simplicity.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...