3

I've been stuck on collision detection and how to handle it for a VERY long time. I need help understanding how to use collision detection with Tiled map editor. I have the TMX file parsed and displayed with a player, camera and keyboard movement. I'm not sure how to about doing to collision. My map has 2 layers, one for grass and tiles you can walk on, the other is objectLayer. I've seen people saying I should loop through all the tiles in an object layer and assign rectangles to them. I've got NO idea how to do that and I'm looking for some insight.

My main question is: how do I loop through my layer and get all the tiles and assign rectangles to them.

Game Class:

    public class Game extends BasicGameState {

Player player;
Camera cam;
Map map = new Map();

public void init(GameContainer container, StateBasedGame sbg) throws SlickException {
    map.init();
    cam = new Camera(0, 0);
    player = new Player(new Image("res/textures/player.png"), container.getWidth() / 2, container.getHeight() / 2, 32, 32);
}

public void update(GameContainer container, StateBasedGame sbg, int delta) throws SlickException {
    cam.tick(player);
    player.update(container, delta, map);
}

public void render(GameContainer container, StateBasedGame sbg, Graphics g) throws SlickException {
    g.translate(-cam.getX(), -cam.getY());
        map.render();
        player.render(g);
    g.translate(cam.getX(), cam.getY());
}

public int getID() {
    return 1;
}

}

Player Class:

    public class Player {

float x, y;

int width, height;
double velX = 0.4;
double velY = 0.4;

Image img;

public Player(Image img, float x, float y, int width, int height) {
    this.img = img;
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
}

public void init() throws SlickException {

}

public void update(GameContainer container, int delta, Map map) {
    Input input = container.getInput();

    if (input.isKeyDown(Input.KEY_D)) x += velX;
    if (input.isKeyDown(Input.KEY_A)) x -= velX;
    if (input.isKeyDown(Input.KEY_W)) y -= velY;
    if (input.isKeyDown(Input.KEY_S)) y += velY;

}

public void render(Graphics g) {
    //g.scale(-2, -2);
    g.drawImage(img, x, y);
}

public float getX() {
    return x;
}

public float getY() {
    return y;
}

}

Map class:

    public class Map {

TiledMap map;

public void init() throws SlickException {
    map = new TiledMap("res/map/zenith.tmx");
}

public void render() throws SlickException {
    map.render(0, 0);
}

}

metropolision
  • 405
  • 4
  • 10
Phippre
  • 53
  • 1
  • 8

2 Answers2

2

I'm not too familiar with the methods exactly for getting the tile, but it's definitely in the Javadoc. http://slick.ninjacave.com/javadoc/

For checking collisions against a player, you need to change your movement to check whether the new location of the player is going to have a collision, otherwise you'll get stuck on them.

public void update(GameContainer container, int delta, Map map) {
    Input input = container.getInput();
    float dx = 0, dy = 0;
    // add to dx and dy instead of moving the player straight away.
    if (input.isKeyDown(Input.KEY_D)) dx += velX;
    if (input.isKeyDown(Input.KEY_A)) dx -= velX;
    if (input.isKeyDown(Input.KEY_W)) dy -= velY;
    if (input.isKeyDown(Input.KEY_S)) dy += velY;
    // here we check the collisions, you can fiddle with this if statement and it'll behave differently.
    if(dx != 0 || dy != 0){
        if(map.checkCollision(x+dx,y+y){
           x+=dx;
           y+=dy;
        }
    }
}

As for checking the collision itself you need a new method in your map class that creates a rectangle for the player and checks against a 2D array inside the map class which holds your collisions. You can populate this array when the map is intialised by looping through your Tiled map through the layer you need and checking if the tile is a collision with getTile(x,y). If the tile has the correct properties for a collision you create a rectangle in that location in your 2D array with something like this:

collisionArray[x][y] = new Rectangle(x,y,TILEWIDTH,TILEHEIGHT);

You can now check for the collision inside

map.checkCollision(x,y)

and with a rectangle created for the player using

Rectangle player = new Rectangle(x,y,PLAYERWIDTH,PLAYERHEIGHT);

Putting it all together you can then check for an intersection with any tiles in the collision array with .intersect(r) as mentioned earlier and map.collision can return true to the player which should stop movement of the player.

I hope that wasn't too convoluted, the javadoc is very helpful and Kevin Glass wrote a thing about checking collisions with just 2D arrays, with varying collision box sizes. http://www.cokeandcode.com/main/tutorials/tile-maps/

Malii
  • 448
  • 6
  • 17
  • 1
    Wow, that was an amazingly detailed answer and well put. Im gonna go try it out right now and see if it works. It makes more sense now, thank you so much. – Phippre Nov 29 '15 at 19:21
  • Im looping through my map on the objectLayer and if it gets a tileid of 1 then it add a rectangle to the array but its giving me some kind of error. Rectangle[][] collisionArray; public void init() throws SlickException { map = new TiledMapPlus("res/map/zenith.tmx"); int objectLayer = map.getLayerIndex("objectLayer"); for (int x = 0; x < map.getWidth(); x++) { for (int y = 0; y < map.getHeight(); y++) { if (map.getTileId(x, y, objectLayer) == 1) { collisionArray[x][y] = new Rectangle(x, y, 32, 32); } } } } – Phippre Nov 30 '15 at 16:12
  • You might need to intialise the collision array with empty Rectangles to loop through it. What's the error? – Malii Nov 30 '15 at 16:33
  • It throws a slick exception so im not sure. how would I go about assign empty rectangles to it. – Phippre Nov 30 '15 at 17:06
  • Omg wait I got it I think. instead of Rectanlge[][] collisionArray; I did Rectangle[][] collisionArray = new Rectangle[60][30]; 60 is the amount of tiles on the X axis I have and 30 is the amount of tiles on Y axis I have. So its running now and I think I have rectangles assigned to them – Phippre Nov 30 '15 at 17:12
1

Ok, now im getting a null pointer exception in my checkCollision()

public void assignRectangles() {
    int objectLayer = map.getLayerIndex("objectLayer");
    for (int x = 0; x < map.getWidth(); x++) {
        for (int y = 0; y < map.getHeight(); y++) {
            if (map.getTileId(x, y, objectLayer) == 1){
                collisionArray[x][y] = new Rectangle(x * 32, y * 32, 32, 32);
            }
        }
    }
}

public boolean checkCollision(float x, float y) {
    for (int j = 0; j < collisionArray.length; j++) {
        for (int i = 0; i < collisionArray[j].length; i++) {
            if (collisionArray[j][i].getX() == x || collisionArray[j][i].getY() == y) {
                return false;
            }
        }
    }
    return true;
}

Im getting it on the line where it say if (collisionArray[j][i].getX() == x || collisionArray[j][i].getY() == y)

Not sure why im getting it though

Phippre
  • 53
  • 1
  • 8
  • Set each of the values in the collisionArray to a new Rectangle of 0,0 in size on the map intialisation or check if the collisionArray value you are getting does not equal null. – Malii Nov 30 '15 at 23:45
  • I looped through the collisionArray and all the values in there are returning null. – Phippre Dec 01 '15 at 14:17