• Advertisement
Sign in to follow this  

Detecting tile collisions in a 2D platformer

This topic is 1242 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

My question is there a better way of detaching collisions. I never done a 2D platformer and I already learned a lot of lessons after solving the coordinate comparisons problem with the main character colliding with the block.

 

I was thinking storing the blocks tiles into a list for a given "game map" class. But then it seems the main character need to go through constantly a list of blocks let say for "24 blocks" which means 24 elements in an array list even though the main character is no where the blocks but is in the game map that contains those blocks. 

 

I love to hear everyone suggestions. I know the below code can be improved. block 1 code is the same code as block 0 code. The difference is the replace block0 name with block1 name and you will see the code is exactly the same.

 

Code is in Java. 

 

 
// block 0 code
if(getX() + idleRightAnim.getWidth() > block.getX() - camera.getX()
&& getX() < block.getX() + block.getWidth() - camera.getX()
&& getY() + idleRightAnim.getHeight() == block.getY() - camera.getY()
&& state != ActionState.RUNNING && state != ActionState.ATTACKING)
{
 
state = ActionState.IDLE;
onSurface = true;
 
 
}
 
if((getX() + idleRightAnim.getWidth() < block.getX() - camera.getX()
|| getX()  > block.getX() + block.getWidth() - camera.getX())
&& getY() + idleRightAnim.getHeight() == block.getY() - camera.getY())
{
onSurface = false;
 
}
 
// block 1 code
if(getX() + idleRightAnim.getWidth() > block1.getX() - camera.getX()
&& getX() < block1.getX() + block1.getWidth() - camera.getX()
&& getY() + idleRightAnim.getHeight() == block1.getY() - camera.getY()
&& state != ActionState.RUNNING && state != ActionState.ATTACKING)
{
 
state = ActionState.IDLE;
onSurface = true;
 
 
}
 
 
if((getX() + idleRightAnim.getWidth() < block1.getX() - camera.getX()
|| getX()  > block1.getX() + block1.getWidth() - camera.getX())
&& getY() + idleRightAnim.getHeight() == block1.getY() - camera.getY())
{
onSurface = false;
 
}
 

 

ScreenShot2014-09-27at105957PM_zps5372b3

Share this post


Link to post
Share on other sites
Advertisement

My reccomendation is to just check each object for collision in a double for loop, and check for collision pairs.

 

However, if this method can be rather slow, since your doing o(n)^2 checks.

 

You may wish to optomize later, and add a broadphase collision test, such as a sweep and prune, or a quad-tree.

 

Whatever floats your boat.

Share this post


Link to post
Share on other sites

For a 2D platformer, I think a good idea would be to have two-dimensional array for tiles.

Then you can do:

ArrayList<Rectangle> collision = new ArrayList<Rectangle>();

int xBegin = playerPos.x / Tile.TILE_WIDTH;
int xEnd   = (playerPos.x + Player.PLAYER_WIDTH) / Tile.TILE_WIDTH;

int yBegin = playerPos.y / Tile.TILE_HEIGHT;
int yEnd   = (playerPos.y + Player.PLAYER_HEIGHT) / Tile.TILE_HEIGHT;

for (int x = xBegin; x <= xEnd; x++) {
 for (int y = yBegin; y<= yEnd; y++) {
  if (tiles[x][y].isSolid()) {
   collision.add(new Rectangle(x * Tile.TILE_WIDTH, y * Tile.TILE_HEIGHT, Tile.TILE_WIDTH, Tile.TILE_HEIGHT);
  }

 }
}

I think the code is self-explanatory.

Then you can iterate through collision and handle collision for each tile (rectangle) separately.

MatejaS

Share this post


Link to post
Share on other sites

Like most people already explained, use an two dimensional array with either an object type or byte.

public static final byte SOLID = 0;
public static final byte HOLLOW = 1;

byte[][] worldTiles = new byte[stageHeight][stageWidth];

And when to check for collision, the fastest way is to treat all entities as rectangles(this is often enough). You then check if the border of the rectangle is colliding with solid tile.

	//Checks if the specified GameObject collides with the given tile
	public boolean collidesWithTile(byte tileType, GameObject go)
	{
		int 	x  = (int) go.x,
			y  = (int) go.y,
			x2 = (int) (go.x + go.width),
		    	y2 = (int) (go.y + go.height);
		
		for(int lx = x; lx < x2; lx++)
		{
			if(worldTiles[y][lx] == tileType || worldTiles[y2][lx] == tileType)
				return true;
		}
		for(int ly = y; ly < y2; ly++)
		{
			if(worldTiles[ly][x] == tileType || worldTiles[ly][l2] == tileType)
				return true;
		}
		return false;
	}

go.x++;
boolean canGoRight = collidesWithTile(HOLLOW, go);
go.x--;

The great thing about byte array is that they take so little space in the ram. An map with 25 million tiles wont take more than 100mb ram.

You could also use a bunch of references instead of byte, which should take less space than byte.

Edited by P0jahn

Share this post


Link to post
Share on other sites

This being Java, there technically are no multi-dimensional arrays -- just one-dimensional arrays of references to other arrays. So instead of a 2-dimensional array I would recommend using a 1-dimensional array and mapping the x/y coordinates to the array indexes. Keeping the tiles in one contiguous chunk of memory will have better cache locality which should give you better performance.

// Define an array large enough to hold everything in one chunk of RAM
byte[] worldTiles = new byte[HEIGHT * WIDTH];

// Map the x, y coordinates to an array index using the following formula:
byte tile = worldTiles[y * HEIGHT + x];

It's basically the same formula a C/C++ compiler would build for you automatically, but Java makes us write it out explicitly. It probably wouldn't hurt to define a class to handle that calculation for you. Perhaps something like this:

public class World {

    private byte[] tiles;
    private int width;
    private int height;

    public World(int width, int height) {
        this.width = width;
        this.height = height;
        this.tiles = new byte[width * height];
    }

    public byte getTile(int x, int y) {
        return tiles[y * height + x];
    }

    public void setTile(int x, int y, byte t) {
        tiles[y * height + x] = t;
    }
}

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement