I cribbed some code from another web tutorial (SuperKoalio) about detecting a collision between my player, and tiles within a tilemap layer called "walls". The game is an RPG (Gauntlet) style game. The collision detection generally works but occasionally, the player will pass through a wall and appear much further away on the map, or suddenly outside the bounds of the map. Its almost like there are gaps.
The code I have used appears to check the tiles immediately to the left and right of the player, and then performs another check on the tiles above and below the player. I think the idea is fundamentally ok, except that this doesnt take into consideration diagonal movement, which I believe might be causing the player to pass through walls.
The code inside my detection routine is as follows:
public void checkCollisions(float delta) {
////////////////////////////////////////////////////////////////
//collision detection - X
int startX, startY, endX, endY;
startX = (int) (position.x);
endX = (int) (position.x + 1);
startY = (int) (position.y);
endY = (int) (position.y);
playerRect = rectPool.obtain();
playerRect.set(position.x, position.y, 1.0f, 1.0f);
getWalls(startX, startY, endX, endY, walls);
for (Rectangle wall : walls)
{
//Rectangle rectangle = rectangleObject.getRectangle();
if (playerRect.overlaps(wall) && velocity.x != 0) {
velocity.x = 0;
if(facingDir == 3) {
position.x = wall.x+1.1f;
} else if(facingDir == 4) {
position.x = wall.x-1.1f;
}
break;
}
// collision happened
//break;
}
playerRect.x = position.x;
// if the player is moving upwards, check the tiles to the top of it's
// top bounding box edge, otherwise check the ones to the bottom
if (velocity.y > 0)
{
startY = (int) (position.y);
endY = (int) (position.y + 1.1f);
}
else
{
startY = endY = (int) (position.y);
}
startX = (int) (position.x);
endX = (int) (position.x + 1);
getWalls(startX, startY, endX, endY, walls);
//playerRect.y = velocity.y;
for (Rectangle wall : walls)
{
if (playerRect.overlaps(wall) && velocity.y != 0)
{
velocity.y = 0;
//velocity.x = 0;
if(facingDir == 1) {
position.y = wall.y - 1.1f;
} else if(facingDir == 2) {
position.y = wall.y + 1.1f;
}
break;
}
break;
}
playerRect.y = position.y;
rectPool.free(playerRect);
velocity.x = 0;
velocity.y = 0;
}
And the "getWalls()" method which loads the walls layer is as follows:
/**
* Get the walls of the map for collision detection between the player and the walls, so that
* the player cannot pass through.
* @param startX
* @param startY
* @param endX
* @param endY
* @param walls
*/
private void getWalls(int startX, int startY, int endX, int endY, Array<Rectangle> walls)
{
TiledMapTileLayer layer = (TiledMapTileLayer) MapRenderer.map.getLayers().get("walls");
layer.setVisible(false);
rectPool.freeAll(walls);
walls.clear();
for (int y = startY; y <= endY; y++)
{
for (int x = startX; x <= endX; x++)
{
TiledMapTileLayer.Cell cell = layer.getCell(x, y);
if (cell != null)
{
Rectangle rect = rectPool.obtain();
rect.set(x, y, 1, 1);
walls.add(rect);
}
}
}
}
Does anyone have any ideas as to why this routine is not very robust?
I really want to be able to achieve this as simply and effectively as possible. The same technique would also need to be applied to enemies, to stop them from also passing through walls.
Surely there must be a better and more reliable way of doing this, or is it just that there are faults/gaps in my code - for example if I it a wall in a diagonal direction?