Custom Tile Collision Detection Has Trouble With Edges

So, I'm writing my own game to expand on my abilities as a programmer. However, I have come to a writer's-block of sorts. The game I am building uses tile collision, but allows the player to be in an unaligned space (like at the edge of a tile rather than directly above it).

The code I have written to determine which tile the player is standing on is failing. On certain edges, when standing on top of the tile, the player will fall through the tile. Here is an example of this occurrence: http://i.imgur.com/CACfhIP.gifv

I have narrowed this down to this specific segment of code: (Please note that SPRITE_WIDTH is 20, which also means 20 pixels)

BOOL changePos(Player* player, Level* level, int newX, int newY)
{
int roundedY;
int roundedX;

if (newX <= player->pos.x)
roundedX = roundDownTo(newX, SPRITE_WIDTH); //round down to a multiple of SPRITE_WIDTH
else if (newX > player->pos.x)
roundedX = roundUpTo(newX, SPRITE_WIDTH); //ditto but up


The problem seems to be that both parts of the if...else if... statement require the equality symbol to work correctly. Meaning that in the provided example, the fall-through glitch will happen to the tile on the left instead if I move the equality symbol to the else if portion.

I attempted to fix this issue by adding an else clause and removing the equality from the if...else if... parts. My method was to determine which half of the tile the player was on and determine if it required them to fall. So, my code became this:

BOOL changePos(Player* player, Level* level, int newX, int newY)
{
int roundedY;
int roundedX;

if (newX < player->pos.x)
roundedX = roundDownTo(newX, SPRITE_WIDTH);
else if (newX > player->pos.x)
roundedX = roundUpTo(newX, SPRITE_WIDTH);

else
if (newX%SPRITE_WIDTH >= SPRITE_WIDTH / 2)
roundedX = roundDownTo(newX, SPRITE_WIDTH);
else
roundedX = roundUpTo(newX, SPRITE_WIDTH);


But this created an even stranger case where the middle part of the tile would cause a fall-through but the edges would work correctly on both the left and the right tile. This can be seen here: http://i.imgur.com/lTaUuzh.gifv

I've been at this all day (literally), I appreciate any help you can provide.

If I have not provided enough information, please let me know and I will do my best to provide it.

My approach was fundamentally flawed. After chancing upon this post I realized that using only a single point and some weird rounding would not work. So, I decided to add four collision points on all four corners of the player. So, I replaced my collision detection code for something that compensates for this.

typedef struct tagPlayer
{
POINT pos;
POINT collisionPoints[4];
int jumpHeight;
unsigned int isJumping : 1;
}Player;

BOOL changePos(Player* player, Level* level, int newX, int newY)
{
for (int i = 0; i < 4; i++)
{
POINT colPoint = player->collisionPoints[i];
if (colPoint.y + newY >= level->levelHeight || colPoint.y + newY < 0 || newX > level->levelWidth - SPRITE_WIDTH || newX < 0)
return TRUE;
int test = roundUpTo(newY - colPoint.y, SPRITE_HEIGHT);
int test2 = roundUpTo(newX - colPoint.x, SPRITE_WIDTH);
TileInfo* info = getTileAtPos(level, test, test2);
if (info->is_collidable)
return TRUE;
}
player->pos.x = newX;
player->pos.y = newY;
}


This works perfectly now. In this example the white dots represent the collision points on the sprite. Notice that collision is now operating correctly. Please note that the collision points I selected were set like this

player.collisionPoints[0].x = 0;
player.collisionPoints[0].y = 0;

player.collisionPoints[1].x = 0;
player.collisionPoints[1].y = SPRITE_HEIGHT - 1;

player.collisionPoints[2].x = SPRITE_WIDTH - 1;
player.collisionPoints[2].y = 0;

player.collisionPoints[3].x = SPRITE_WIDTH - 1;
player.collisionPoints[3].y = SPRITE_HEIGHT - 1;


SPRITE_WIDTH and SPRITE_HEIGHT are both equal to 20. The -1 is to make sure that they are on the correct parts of the sprite for the white dots that represent the collision points. I believe that it would operate just fine without it, but I have not tested that and I have no plans to. I hope this helps anyone else creating custom tile collision in the future.

• The collision point method is a common practice in platformers. If your cahracter isn't a square, you can place the collisionpoints closer to the character's feet and body. As a bonus, you can alter the animation to make a character do a "balance" animation if just one of the two ground collision points collide. For example: image of Sonic balancing because only the right collision point collides. Commented Jul 12, 2016 at 9:23