Collision detection with tiling, fall problems?

Evening

I’m making a block-based game, yeah I know, using tiling. As it’s convenient to do so I hold all the tiles in a two-dimensional array. It seems to work very well together with having my images for the tiles held in an enum, so they’re only loaded once. It works quickly for Java 2D. Everything including the sky in my game is made of tiles apart from the player.

I have a problem with making my player, a robot, fall. It’s fine for horizontal collisions but downwards is a problem if the robot is slightly spanning two tiles. I fixed this a bit by making the robot 48px tall and 24px wide when tiles are 32px by 32px but it’s still bad:

If I nudge forwards a tiny bit more it’s fine a the robot will fall. Here’s my code:

// Find the tile the robot spans.
int t = player.getY1() / BLOCK_SIZE; // Top tile coordinate.
int b = (player.getY2() / BLOCK_SIZE); // Bottom tile coordinate.
int l = player.getX1() / BLOCK_SIZE; // Left tile coordinate.
int r = player.getX2() / BLOCK_SIZE; // Right tile coordinate.

// Left movement, -1 to get both tiles on the sides.
if(gameInput.getKeyState(KeyEvent.VK_D) && !blocks[b - 1][r].isSolid() && !blocks[t][r].isSolid()) {
    player.move(1);
}

// Right movement, -1 to get both tiles on the sides.
if(gameInput.getKeyState(KeyEvent.VK_A) && !blocks[b - 1][l].isSolid() && !blocks[t][l].isSolid()) {
    player.move(-1);
}

// Jumping, well more like flying at the moment.
if(gameInput.getKeyState(KeyEvent.VK_W)) {
    player.jump(3);
}

// Falling. This is mostly what's the problem. isSolid() simply returns true unless the tile is sky.
if(!blocks[b][l].isSolid() && !blocks[b][r].isSolid()) {
    player.fall(1);
}

I recall other games with similar problems but can’t recall if they work around it or leave it. Does anyone have any suggestions? ???

Currently, you are getting the the collisions based on two different tiles, when your character is only less than one tile wide.

// Find the tile the robot spans.
int t = player.getY1() / BLOCK_SIZE; // Top tile coordinate.
int b = (player.getY2() / BLOCK_SIZE); // Bottom tile coordinate.
int l = player.getX1() / BLOCK_SIZE; // Left tile coordinate.
int r = player.getX2() / BLOCK_SIZE; // Right tile coordinate.

if the width of the player is less that the tile width, I would only check the center of the player with collision with the ground. So maybe add another variable to your tiles to make it:

// Find the tile the robot spans.
int t = player.getY1() / BLOCK_SIZE; // Top tile coordinate.
int b = (player.getY2() / BLOCK_SIZE); // Bottom tile coordinate.
int l = player.getX1() / BLOCK_SIZE; // Left tile coordinate.
int r = player.getX2() / BLOCK_SIZE; // Right tile coordinate.
int m = ((player.getX2() + player.getX1()) / 2 ) /  BLOCK_SIZE; // The NEW middle tile coordinate

And then change your fall code so that it uses that variable instead:

if(!blocks[b][m].isSolid()) { 
   player.fall(1);
}

With these changes, you may see slight overlap of the character on the wall that he just falls over, but he should be able to move out of it. Just looking at your collisions code, it looks like walls will be troublesome for you as you can’t quite get very close to them.

Edit: changed a minus to a plus.

Easy option: make the base of the robot be its widest part.

Nice option: have a width (e.g. 24px) and an effective footprint (corresponding to the graphic; e.g. 18px). If the effective footprint is above empty space, the robot falls, but you nudge it sideways if necessary to prevent its width from passing through solid space.

Thanks for you’re suggestions, I’ve just reworked the code to have separate timing for ticks and frames and it’s not yet ready to run but once it is I’ll give you suggestion a go. I realise I won’t get this perfect but just a bit better will be fine.