使用Java、Slick2D和Tiled Map Editor进行碰撞检测

3

我已经卡在碰撞检测和如何处理它上面很长时间了。我需要帮助理解如何与Tiled地图编辑器一起使用碰撞检测。我已经解析了TMX文件,并使用玩家、相机和键盘移动显示出来。我不确定如何进行碰撞检测。我的地图有2个层,一个是草和可以行走的瓷砖,另一个是对象层。我看到有人说我应该循环遍历对象层中的所有瓷砖并分配矩形给它们。我不知道如何做到这一点,正在寻求一些见解。

我的主要问题是:如何循环遍历我的层并获取所有瓷砖并将其分配给矩形。

游戏类:

    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;
}

玩家类:

    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 类:

    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);
}

}

2个回答

2

我不太熟悉获取瓦片的具体方法,但在Javadoc中肯定有说明。http://slick.ninjacave.com/javadoc/

要检查与玩家的碰撞,您需要更改移动方式以检查玩家的新位置是否会发生碰撞,否则您将被卡住。

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;
        }
    }
}

关于检查碰撞本身,您需要在地图类中创建一个新方法,为玩家创建一个矩形,并针对地图类内部保存碰撞的2D数组进行检查。您可以通过循环遍历Tiled地图中所需的层,并使用getTile(x,y)检查瓷砖是否与碰撞相匹配来填充此数组。如果瓷砖具有用于碰撞的正确属性,则可以在该位置上创建一个矩形,例如:

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

您现在可以在内部检查碰撞。
map.checkCollision(x,y)

通过创建一个针对玩家的矩形来实现

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

将所有内容结合起来,您可以使用之前提到的.intersect(r)检查与碰撞数组中任何瓦片的交集,并且map.collision可以向玩家返回true,这应该会停止玩家的移动。
希望这不会太复杂,javadoc非常有用,Kevin Glass编写了一篇关于仅使用2D数组检查碰撞的文章,其中包括不同的碰撞框大小。 http://www.cokeandcode.com/main/tutorials/tile-maps/

1
哇,那是一個非常詳細且表述得很好的答案。我現在要去嘗試一下,看看它是否有效。現在它更合理了,非常感謝你。 - Phippre
我正在遍历对象层上的地图,如果它得到一个tileid为1,那么就将一个矩形添加到数组中,但是它给了我一些错误。 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
你可能需要用空的矩形初始化碰撞数组以便循环遍历。出了什么错误? - Malii
它抛出了一个漂亮的异常,所以我不确定该怎么给它分配空矩形。 - Phippre
哦,等等,我想我明白了。我用的是Rectangle[][] collisionArray;,但应该改成Rectangle[][] collisionArray = new Rectangle[60][30];60 是我在X轴上拥有的瓷砖数量,30 是我在Y轴上拥有的瓷砖数量。现在它正在运行,我认为已经将矩形分配给它们了。 - Phippre

1

好的,现在我在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;
}

我在这行代码上遇到了问题,它说的是:如果(collisionArray[j][i].getX() == x || collisionArray[j][i].getY() == y),我正在寻找解决方法。

但不确定为什么会出现这种情况。


在地图初始化时,将collisionArray中的每个值设置为一个新的大小为0,0的矩形,或者检查您获取的collisionArray值是否不等于null。 - Malii
我遍历了collisionArray,里面的所有值都返回null。 - Phippre

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接