macalaca
macalaca

Reputation: 1048

java swing - how to determine the side of a collision between two rectangles

I'm building a top-down 2D scroller using java swing with very similar gameplay to that of the classic game Bomberman, where the player can move the character in all 4 cardinal directions.

All the game objects have instance variables containing the (x,y) coordinates of where they are to be rendered on the JPanel, they also have instance variables for the previous (x,y) coordinate where they were rendered in the last frame. My collision detection algorithm essentially checks to see if the player object intersects one of the walls present in the grid every time we refresh the screen - This can be seen in the bit of code below:

if (playerRectangle.intersects(wallRectangle)) {
  player.restorePreviousPosition();
}

The restorePreviousPosition() method sets the (x,y) coordinates of the player to what they were before the collision occurred. This methods works fine, it prevents the player from going through objects. However, the controls seem very jagged because if the player is, for example, trying to move left and up while in contact with a wall, the character remains still. In other words, the character can't touch a wall and still move parallel to it. In order to fix this problem I created four rectangles drawn on top of the player object that I use to determine the side that is colliding with another object, so that instead of restoring the (x,y) coordinates I can restore x or y depending on which side is colliding. For example, if I know that the top is colliding with something, then I can restore the y position and allow the x to change freely. Below you can see a picture of the player object with the rectangles drawn on it:

enter image description here

This idea is very unreliable, specially in corners where it is difficult to determine from which side the collision is coming from. So, my question is, how can I reliably determine which side of a rectangle is colliding with another rectangle in java using swing? Or, do you have a better alternative to my approach towards detecting collisions?

Note that player movement is completely free, my implementation doesn't snap the character from tile to tile. And that the distance between tiles is 32x32 pixels, and the player is 30x30 pixels.

Thank you for the help. If you have any extra questions about my implementation, please let me know.

Upvotes: 3

Views: 2786

Answers (3)

Acidic Army
Acidic Army

Reputation: 11

rects.stream().forEach((re) ->
{
    double w = 0.5 * (re.width + player.width);
    double h = 0.5 * (re.height + player.height);
    double dx = re.getCenterX() - player.getCenterX();
    double dy = re.getCenterY() - player.getCenterY();

    if (abs(dx) < w && abs(dy) < h)//collision
    {
        double wy = w * dy;
        double hx = h * dx;
        if (wy >= hx)
            if (wy >= -hx)//top of block
                player.y = re.y - player.height;//align edges
            else//right of block
                player.x = re.x + re.width;//align edges
        else
            if (wy >= -hx)//left of block
                player.x = re.x - player.width;//align edges
            else//bottom of block
                player.y = re.y + re.height;//align edges
    }
});

This will find which side is being hit and correct the player's position to line up with that side's edge. I am using a slightly more complicated version of this code in my game, but the idea is the same. (do this AFTER you reposition your player in your updating method.)

Upvotes: 1

Chris
Chris

Reputation: 694

One really simple solution (given what you already have written) is to split the code for Test-and-Move into two checks.

//update.player.x.position
if (playerRectangle.intersects(wallRectangle)) {
  player.restorePreviousPosition();
}

//update.player.y.position
if (playerRectangle.intersects(wallRectangle)) {
  player.restorePreviousPosition();
}

Upvotes: 1

NESPowerGlove
NESPowerGlove

Reputation: 5496

Kind of simple, take a collision that happens at the bottom right corner of some rectangle A by B, and you want to find out if B collided into A more from the right, or more from the bottom. All you have to do is measure how many pixels the top y coordinate of B is above A's lowest y coordinate, and measure how many pixels B's far left x coordinate is to the left of A's far right x coordinate.

If there's more of a difference between the x coordinates than the y coordinates then it's a collision from the bottom, otherwise it's a collision from the side.

Upvotes: 0

Related Questions