0

How can I manage collisions in slick ?

I created map and every tile which is wall have option blocked = true;

How to implement collision, that if I hit wall, I can move trough it? Also if I reach edge of map, block movement.

package javagame;

import org.newdawn.slick.Animation;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.BasicGameState;
import org.newdawn.slick.state.StateBasedGame;
import org.newdawn.slick.tiled.TiledMap;

public class Play extends BasicGameState {

    Animation movingUp, movingDown, movingLeft, movingRight;
    Animation player;
    int[] duration = {200, 200, 200};
    float playerX = 0;
    float playerY = 0;
    float cameraX;
    float cameraY;
    float screenWidth;
    float screenHeight;
    private TiledMap map = null;
    private static final float SPEED = 0.1f;

    private boolean[][] blocked;

    public Play(int state, int x, int y) {
        playerX = (float) x;
        playerY = (float) y;
    }

    private boolean blocked(float x, float y) {
        return blocked[(int) x][(int) y];
    }

    @Override
    public void init(GameContainer gc, StateBasedGame sbg) throws SlickException {

        map = new TiledMap("map/map.tmx");

        // build a collision map based on tile properties in the TileD map 
        blocked = new boolean[map.getWidth()][map.getHeight()];
        for (int x = 0; x < map.getWidth(); x++) {
            for (int y = 0; y < map.getHeight(); y++) {
                int tileID = map.getTileId(x, y, 0);
                String value = map.getTileProperty(tileID, "blocked", "false");
                if ("true".equals(value)) {
                    blocked[x][y] = true;
                }
            }
        }

        Image[] walkUp = {
            new Image("graphics/player/up0.png"),
            new Image("graphics/player/up1.png"),
            new Image("graphics/player/up2.png")
        };

        Image[] walkDown = {
            new Image("graphics/player/down0.png"),
            new Image("graphics/player/down1.png"),
            new Image("graphics/player/down2.png")
        };
        Image[] walkLeft = {
            new Image("graphics/player/left0.png"),
            new Image("graphics/player/left1.png"),
            new Image("graphics/player/left2.png")
        };
        Image[] walkRight = {
            new Image("graphics/player/right0.png"),
            new Image("graphics/player/right1.png"),
            new Image("graphics/player/right2.png")
        };

        movingUp = new Animation(walkUp, duration, false);
        movingDown = new Animation(walkDown, duration, false);
        movingLeft = new Animation(walkLeft, duration, false);
        movingRight = new Animation(walkRight, duration, false);

        player = movingDown;

    }

    @Override
    public void render(GameContainer gc, StateBasedGame sbg, Graphics g) throws SlickException {

        screenWidth = gc.getWidth();
        screenHeight = gc.getHeight();

        cameraX = (screenWidth / 2) - (playerX / 2);
        cameraY = (screenHeight / 2) - (playerY / 2);

        map.render((int) playerX, (int) playerY);

        player.draw(cameraX, cameraY);

        g.drawString("X: " + playerX + "\nY: " + playerY, 520, 20);
        g.resetTransform();

    }

    @Override
    public void update(GameContainer gc, StateBasedGame sbg, int delta) throws SlickException {
        Input input = gc.getInput();

        if (input.isKeyDown(Input.KEY_UP) || input.isKeyDown(Input.KEY_W)) {
            player = movingUp;
            playerY += delta * SPEED;
            player.update(delta);
        } else if (input.isKeyDown(Input.KEY_DOWN) || input.isKeyDown(Input.KEY_S)) {
            player = movingDown;
            playerY -= delta * SPEED;
            player.update(delta);
        } else if (input.isKeyDown(Input.KEY_LEFT) || input.isKeyDown(Input.KEY_A)) {
            player = movingLeft;
            playerX += delta * SPEED;
            player.update(delta);
        } else if (input.isKeyDown(Input.KEY_RIGHT) || input.isKeyDown(Input.KEY_D)) {
            player = movingRight;
            playerX -= delta * SPEED;
            player.update(delta);
        }

    }

}

Here is sample source code (NetBeans project):

https://www.box.com/s/9nicp0n067de632kcpj3

Quaternion
  • 10,380
  • 6
  • 51
  • 102
Ing. Michal Hudak
  • 5,338
  • 11
  • 60
  • 91

2 Answers2

2

On Update, you can simply check the array. If it is a wall don't update. If it is the border, don't update.

@Override
public void update(GameContainer gc, StateBasedGame sbg, int delta) throws SlickException {
    Input input = gc.getInput();
    float yChange = 0, xChange=0;
    boolean capturedInput = false;
    if (input.isKeyDown(Input.KEY_UP) || input.isKeyDown(Input.KEY_W)) {
        player = movingUp;
        yChange += delta * SPEED;
        capturedInput = true;
    } else if (input.isKeyDown(Input.KEY_DOWN) || input.isKeyDown(Input.KEY_S)) {
        player = movingDown;
        yChange -= delta * SPEED;
        capturedInput = true;
    } else if (input.isKeyDown(Input.KEY_LEFT) || input.isKeyDown(Input.KEY_A)) {
        player = movingLeft;
        xChange += delta * SPEED;
        capturedInput = true;
    } else if (input.isKeyDown(Input.KEY_RIGHT) || input.isKeyDown(Input.KEY_D)) {
        player = movingRight;
        xChange -= delta * SPEED;
        capturedInput = true;
    }
    if(capturedInput==true && !blocked(playerX+xChange,playerY+yChange)){
        playerX += xChange;
        playerY += yChange;
        player.update(delta);
    }
}

private boolean blocked(float x, float y) {
    return blocked[(int) x][(int) y];
}

Pretty much, do not change the value until you check it first and then change the player value if all is okay.

EDIT: I added a variable capturedInput, this will let me know that we actually have a keyDown event. There are two comments about this. I consider this a "hack" to make your code work and not the most beautiful. I would personally try to add an event listener to the window to listen for key clicks, when a key is clicked I would change the position information, all the while the frame is refreshing and will update, or only refresh now that there was a change by calling repaint().

There are lots of tutorials out there for listening to onKeyDown. Your code should work better now and won't need the manual catch for the edge like your doing, you should be able to draw a wall around the edge in the block array of booleans, if not you can continue with your method.

msj121
  • 2,812
  • 3
  • 28
  • 55
0

Try creating a Rectangle of your blocks from X and Y position multiplied by tile dimensions and then verify with the intersects() method if it collides with your entity, such as explained in wiki Slick.

I hope that's useful to you.

Bart
  • 19,692
  • 7
  • 68
  • 77
Null
  • 11
  • 1