最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

java - Collision detection issue with entity speed in 2D Top-Down RPG Game - Stack Overflow

programmeradmin8浏览0评论

I’m working on a 2D Top-Down RPG and facing an issue with my collision detection. I have two classes: Entity.java and CollisionManager.java.

The Entity class contains attributes like speed and a BoundingBox (represented by a Rectangle object). Before moving the entity, I call a method from the CollisionManager class to check for collisions with the tiles.

The problem is that when I increase the entity’s speed, the collision is detected too early, a few pixels before the entity actually reaches the tile.

CollisionManager.java (checkTile method):

    public void checkTile(Entity entity) {
        int entityLeftWorldX = entity.worldX + entity.boundingBox.x;
        int entityRightWorldX = entityLeftWorldX + entity.boundingBox.width; // Cambiato per maggiore chiarezza
        int entityTopWorldY = entity.worldY + entity.boundingBox.y;
        int entityBottomWorldY = entityTopWorldY + entity.boundingBox.height; // Cambiato per maggiore chiarezza


        int entityLeftColumn = entityLeftWorldX / wm.TILE_SIZE;
        int entityRightColumn = entityRightWorldX / wm.TILE_SIZE;
        int entityTopRow = entityTopWorldY / wm.TILE_SIZE;
        int entityBottomRow = entityBottomWorldY / wm.TILE_SIZE;

        switch (entity.currentDirection) {
            case UP -> entityTopRow = (entityTopWorldY - entity.speed) / wm.TILE_SIZE;
            case DOWN -> entityBottomRow = (entityBottomWorldY + entity.speed) / wm.TILE_SIZE;
            case LEFT -> entityLeftColumn = (entityLeftWorldX - entity.speed) / wm.TILE_SIZE;
            case RIGHT -> entityRightColumn = (entityRightWorldX + entity.speed) / wm.TILE_SIZE;
        }

        int[] topTiles = {
                wm.firstLayerMap.GAME_MAP[entityLeftColumn][entityTopRow],
                wm.firstLayerMap.GAME_MAP[entityRightColumn][entityTopRow]
        };

        int[] bottomTiles = {
                wm.firstLayerMap.GAME_MAP[entityLeftColumn][entityBottomRow],
                wm.firstLayerMap.GAME_MAP[entityRightColumn][entityBottomRow]
        };

        if (isTileColliding(topTiles) || isTileColliding(bottomTiles)) {
            entity.isColliding = true;
        }
    }

    private boolean isTileColliding(int... tileNums) {
        for (int tileNum : tileNums) {
            if (tileNum >= 0 && isTileSolid(tileNum, wm.firstLayerMap)) {
                return true;
            }
        }
        return false;
    }

    private boolean isTileSolid(int tileNum, TileManager layer) {
        return layer != null && layer.tileMap.get(tileNum) != null && layer.tileMap.get(tileNum).isSolid;
    }

Call to the method in Entity.java:

private void updatePosition() {
        if(currentState == State.WALKING) {
            isColliding = false;

            cm.checkTile(this);

            if(!isColliding) {
                switch(currentDirection) {
                    case UP -> worldY -= speed;
                    case DOWN -> worldY += speed;
                    case LEFT -> worldX -= speed;
                    case RIGHT -> worldX += speed;
                }
            }
        }
    }

I’m working on a 2D Top-Down RPG and facing an issue with my collision detection. I have two classes: Entity.java and CollisionManager.java.

The Entity class contains attributes like speed and a BoundingBox (represented by a Rectangle object). Before moving the entity, I call a method from the CollisionManager class to check for collisions with the tiles.

The problem is that when I increase the entity’s speed, the collision is detected too early, a few pixels before the entity actually reaches the tile.

CollisionManager.java (checkTile method):

    public void checkTile(Entity entity) {
        int entityLeftWorldX = entity.worldX + entity.boundingBox.x;
        int entityRightWorldX = entityLeftWorldX + entity.boundingBox.width; // Cambiato per maggiore chiarezza
        int entityTopWorldY = entity.worldY + entity.boundingBox.y;
        int entityBottomWorldY = entityTopWorldY + entity.boundingBox.height; // Cambiato per maggiore chiarezza


        int entityLeftColumn = entityLeftWorldX / wm.TILE_SIZE;
        int entityRightColumn = entityRightWorldX / wm.TILE_SIZE;
        int entityTopRow = entityTopWorldY / wm.TILE_SIZE;
        int entityBottomRow = entityBottomWorldY / wm.TILE_SIZE;

        switch (entity.currentDirection) {
            case UP -> entityTopRow = (entityTopWorldY - entity.speed) / wm.TILE_SIZE;
            case DOWN -> entityBottomRow = (entityBottomWorldY + entity.speed) / wm.TILE_SIZE;
            case LEFT -> entityLeftColumn = (entityLeftWorldX - entity.speed) / wm.TILE_SIZE;
            case RIGHT -> entityRightColumn = (entityRightWorldX + entity.speed) / wm.TILE_SIZE;
        }

        int[] topTiles = {
                wm.firstLayerMap.GAME_MAP[entityLeftColumn][entityTopRow],
                wm.firstLayerMap.GAME_MAP[entityRightColumn][entityTopRow]
        };

        int[] bottomTiles = {
                wm.firstLayerMap.GAME_MAP[entityLeftColumn][entityBottomRow],
                wm.firstLayerMap.GAME_MAP[entityRightColumn][entityBottomRow]
        };

        if (isTileColliding(topTiles) || isTileColliding(bottomTiles)) {
            entity.isColliding = true;
        }
    }

    private boolean isTileColliding(int... tileNums) {
        for (int tileNum : tileNums) {
            if (tileNum >= 0 && isTileSolid(tileNum, wm.firstLayerMap)) {
                return true;
            }
        }
        return false;
    }

    private boolean isTileSolid(int tileNum, TileManager layer) {
        return layer != null && layer.tileMap.get(tileNum) != null && layer.tileMap.get(tileNum).isSolid;
    }

Call to the method in Entity.java:

private void updatePosition() {
        if(currentState == State.WALKING) {
            isColliding = false;

            cm.checkTile(this);

            if(!isColliding) {
                switch(currentDirection) {
                    case UP -> worldY -= speed;
                    case DOWN -> worldY += speed;
                    case LEFT -> worldX -= speed;
                    case RIGHT -> worldX += speed;
                }
            }
        }
    }
Share Improve this question edited Nov 18, 2024 at 17:59 Luca Facchini asked Nov 18, 2024 at 17:48 Luca FacchiniLuca Facchini 374 bronze badges 5
  • 3 Hi there, please add your code in the body of your question, not through a link. Please check the How To Ask section. – Je Campos Commented Nov 18, 2024 at 17:53
  • Hello, sorry about that. Thanks :) I added it. – Luca Facchini Commented Nov 18, 2024 at 18:06
  • You are checking the if (isTileColliding(topTiles) || isTileColliding(bottomTiles)) after you already moved the positions in your Entity, you should do that before moving? – Je Campos Commented Nov 18, 2024 at 18:47
  • 1 Hello Luca, I think the problem is that you are using velocities greater than 1 px, so, if you have velocity = 5, and before moving you check if there is collision, you will not move and you will stay 5 px from the object, instead, if you check after moving, you will be superimposed to the object, therefore, use velocities equal or less than 1. – Marce Puente Commented Nov 18, 2024 at 20:17
  • The java.awt.Rectangle class has an intersects method. – Gilbert Le Blanc Commented Nov 18, 2024 at 20:29
Add a comment  | 

1 Answer 1

Reset to default 0

The problem is that the character doesn't move if the collision is detected; instead, its position should be adjusted just enough to not make it clip into the tiles.

发布评论

评论列表(0)

  1. 暂无评论