Java: Tile-based Collision Detection with Rectangles

There are a few tile-based collision detection questions out there, but none seemed to suit my need.

I guess I really would like to know why this method for collision detection isn't working.

It updates in a tick() method, but I've also tried updating it in the initializing method. The checkCollisions() starts in a player class, and I'm simply using the returned boolean value to check whether the player can move in that direction or not.

public Boolean checkCollision(Player player, Screen screen, Level level)
{
boolean collides = false;

for(int i = 0; i < level.tiles.length; i++)
{
if(player.getBounds().intersects(level.getBounds(level, i)) && Tile.tiles[i].id == 1)
{
collides = true;
}
}

return collides;
}


And it's simply an if statement in the tick() method:

if(input.right)
{
if(Ash.checkCollision(Ash, screen, level) == false && (dirFacing == 2 || dirFacing == 4))
{
xScroll++;
}
if(deltaMS > 16)
{
dirFacing = 2;
}
if(deltaMS <= 16)
{
dirFacing = 4;
}
}


Here's my getBounds for the tiles (I stuck it in my Level class because the tiles are created there):

public int getX(int i)
{
return tileX[i];
}

public int getY(int i)
{
return tileY[i];
}

public Rectangle getBounds(Level level, int i)
{
return new Rectangle(getX(i), getY(i), 16, 16);
}


And I'm trying this method to find the tile's coordinates so I can draw each rectangle for each tile:

public void setTileCoords(int[] tiles)
{
int sum = 0;
for(int i = 0; i < tiles.length; i++)
{
tileX[i] += sum;
tileY[i] += sum;

sum += 16;
}
}


Now I feel it's the way I'm rendering the map, since when I try to expand the map to 32 by 32 pixels (which will render 32 by 32 tiles on screen) it sticks with the 16 by 16 tiles but keeps my little rocky outcrop in the top right corner of the map...

Here's my level class where I do some extreme rendering/arrays of tile coordinates:

public class Level
{
int w;
int h;

public int[] tiles;

public int[] tileX;
public int[] tileY;

public Level(int w, int h)
{
this.w = w;
this.h = h;

tiles = new int[w * h];

tileX = new int[Tile.tiles.length];
tileY = new int[Tile.tiles.length];

setTileCoords(tiles);

}

public void renderBkgrd(int xScroll, int yScroll, Screen screen)
{
//Bitshifting: 10101000 >> 4  = 1010
int xo = xScroll >> 4;
int yo = yScroll >> 4;

int w = (screen.w + 15) >> 4;
int h = (screen.h + 15) >> 4;

screen.setOffs(xScroll, yScroll);

for(int y = yo; y <= h + yo; y++)
{
for(int x = xo; x <= w + xo; x++)
{
getTile(x, y).render(x, y, screen);
}
}

screen.setOffs(0, 0);
}

public Tile getTile(int x, int y)
{
if(x < 0 || y < 0 || x >= w || y >= h) return Tile.rockTile;
return Tile.tiles[tiles[x + y * w]];
}

//Takes tile and offsets.
public void loadMap(int x0, int y0, int x1, int y1)
{
Sprite sprite = SpritesManage.Map1[x0][y0];

for(int y = 0; y < sprite.h; y++)
{
for(int x = 0; x < sprite.w; x++)
{
if(sprite.pixels[x + y * sprite.h] == -9276814)
{
tiles[x + x1 + (y + y1) * h] = Tile.rockTile.id;
}
if(sprite.pixels[x + y * sprite.h] == -11731200)
{
tiles[x + x1 + (y + y1) * h] = Tile.grassTile.id;
}
}
}
}

public void setTileCoords(int[] tiles)
{
int sumX = 0;
int sumY = 0;

for(int y = 0; y < Tile.tiles.length; y++)
{
tileY[y] += sumY;

for(int x = 0; x < Tile.tiles.length; x++)
{
tileX[x] = sumX;

sumX += 16;
}

sumX = 0;
sumY += 16;
}
}

public int getX(int i)
{
return tileX[i];
}

public int getY(int i)
{
return tileY[i];
}

public Rectangle getBounds(Level level, int i)
{
return new Rectangle(getX(i), getY(i), 16, 16);
}
}

• I take it the 'checkCollision' is always coming back false? – Darren Reid Aug 2 '12 at 5:39
• First, we don't know (at least i don't know) where the Tile and Player method come from. Maybe you should expose at least the concerned methods of those class. Regards. – nathan Aug 2 '12 at 7:07
• @Layoric I guess that's the part I'm unsure about, but I'm assuming so as you said. I updated the code. – A13X Aug 3 '12 at 21:17
• Okay, I think I've narrowed the new Null-Pointer Exception to the statement after the && operator in the checkCollision(). I think it needs to be level.tiles[i].id but tiles[] in the level class is an int[]. Could it be the static reference to the rockTile? I've tried the id of the rockTile too. – A13X Aug 4 '12 at 1:51
• It's also important to note that when I use certain statements ALL tiles are blocked (To test this I move right and am blocked). So the collision detection methods and all seem to work, just not in the correct manner. – A13X Aug 4 '12 at 14:11

In

public Boolean checkCollision(Player player, Screen screen, Level level)
{
for(int i = 0; i < level.tiles.length; i++)
{
if(player.getBounds().intersects(Tile.rockTile.getBounds(Tile.tiles, i)) && Tile.tiles[i].equals(Tile.rockTile))
{
return true;
}
else
{
return false;
}
}

return false;
}


You return true if the first tile is colliding, and return false if the first is not.

This exits you from the method. You need to do something like this:

public Boolean checkCollision(Player player, Screen screen, Level level)
{
boolean collides = false;

for(int i = 0; i < level.tiles.length; i++)
{
if(player.getBounds().intersects(Tile.rockTile.getBounds(Tile.tiles, i)) && Tile.tiles[i].equals(Tile.rockTile))
collides = true;
}

return collides;
}


And if you needed to incorporate a position into that, you could add in a few more variables (integer lists, I believe), that you push the co-ordinates of the collision onto.

• Hmmm I'm getting some Null-Pointer Exceptions when I add getX and Y methods to my level class. I'll keep you posted if I make any headway. – A13X Aug 3 '12 at 3:27
• Still haven't gotten the if statement to work without a null exception...I'm unsure of the problem. – A13X Aug 5 '12 at 17:04
• Hmmm. I'll look into it. – Brendan Aug 5 '12 at 20:48

Without viewing rest of your code this is a slight guess however

Tile.rockTile.getBounds(Tile.tiles, i)


Tile.rockTile seems like a static value so your bounds may not be changing (ie. x and y are not changed per tile), However level.tiles would make sense as for each tile in that level it would have a different bounds.

Intersects works when 1 rectangle overlaps another this means on a very basic level that corner of the Rectangle is within the other rectangle.