Search code examples
javacollisionrectanglespacman

Rectangle Wall collision in Java


I really could use some help in order to find a working solution for my game. My game is almost done, but the walls in my game are still not working as they should.

I have tried to find a solution on the internet for this problem, but i still haven't found a simple way to stop a rectangle just before it will collide with a wall (another rectangle).

Right now i have implemented a collision detection between the player rectangle and the wall rectangle and then stopped it to move, but then it gets stuck inside a wall when it hits.

Want it to stop just before, so it still can move. The code i have done this with so far is here:

Pacman Class

public class Pacman {

private String pacmanup = "pacmanup.png";
private String pacmandown = "pacmandown.png";
private String pacmanleft = "pacmanleft.png";
private String pacmanright = "pacmanright.png";

private int dx;
private int dy;
private int x;
private int y;
private int width;
private int height;
private boolean visible;

private Image imageup;
private Image imagedown;
private Image imageleft;
private Image imageright;

public Pacman() {

    ImageIcon i1 = new ImageIcon(this.getClass().getResource(pacmanup));
    imageup = i1.getImage();

    ImageIcon i2 = new ImageIcon(this.getClass().getResource(pacmandown));
    imagedown = i2.getImage();

    ImageIcon i3 = new ImageIcon(this.getClass().getResource(pacmanleft));
    imageleft = i3.getImage();

    ImageIcon i4 = new ImageIcon(this.getClass().getResource(pacmanright));
    imageright = i4.getImage();

    width = imageup.getWidth(null);
    height = imageup.getHeight(null);
    visible = true;
    x = 270;
    y = 189;

}

public int getDx() {
    return dx;
}

public void setDx(int dx) {
    this.dx = dx;
}

public int getDy() {
    return dy;
}

public void setDy(int dy) {
    this.dy = dy;
}

public int getX() {
    return x;
}

public int getY() {
    return y;
}

public void setX(int x) {
    this.x = x;
}

public void setY(int y) {
    this.y = y;
}

public Image getImageup() {
    return imageup;
}

public Image getImagedown() {
    return imagedown;
}

public Image getImageleft() {
    return imageleft;
}

public Image getImageright() {
    return imageright;
}

public void setVisible(boolean visible) {
    this.visible = visible;
}

public boolean isVisible() {
    return visible;
}

public Rectangle getBounds() {
    return new Rectangle(x, y, width, height);
}

public void move() {

    x += dx;
    y += dy;
}

public void keyPressed(KeyEvent e) {

    int key = e.getKeyCode();

    if (key == KeyEvent.VK_LEFT) {
        dx = -2;
        dy = 0;
    }

    if (key == KeyEvent.VK_RIGHT) {
        dx = 2;
        dy = 0;
    }

    if (key == KeyEvent.VK_UP) {
        dx = 0;
        dy = -2;
    }

    if (key == KeyEvent.VK_DOWN) {
        dx = 0;
        dy = 2; 
    }
}

Here i have created a Rectangle getBounds method which i use to create an rectangle of the pacman and place an image over it.

Barrier class / Wall class

public class Barrier {

private String barrier = "barrier.png";

private int x;
private int y;
private int width;
private int height;
private boolean visible;
private Image image;

public Barrier(int x, int y) {
    ImageIcon ii = new ImageIcon(this.getClass().getResource(barrier));
    image = ii.getImage();      
    width = image.getWidth(null);
    height = image.getHeight(null);
    visible = true;
    this.x = x;
    this.y = y;
}

public int getX() {
    return x;
}

public int getY() {
    return y;
}

public boolean isVisible() {
    return visible;
}

public void setVisible(Boolean visible) {
    this.visible = visible;
}

public Image getImage() {
    return image;
}

public Rectangle getBounds() {
    return new Rectangle(x, y, width, height);
}

This class also have the Rectangle getBounds class which i use to detect collision.

The last code i show is how i do the collision detection so far:

Code inside Board class

Rectangle r3 = pacman.getBounds();

 for (int j = 0; j<barriers.size(); j++) {
        Barrier b = (Barrier) barriers.get(j);
        Rectangle r4 = b.getBounds();

        if (r3.intersects(r4)) {
            System.out.println("Wall hit");
            pacman.setDx(0);
            pacman.setDy(0);
        }
    }

Well, what i do, if there is a collision between r3 and r4, i gonna set Dx and Dy to 0.. what i want to find another solution so it detect for collision but i wont get stuck inside a wall, but i don't know how to :/

Hope someone will help.


Solution

  • There are two approaches you can follow. One is ugly but easier, the other one requires a deeper redesign of your classes.

    1) Ugly/Simple Approach

    In the ugly one, you keep moving your guy before doing the collision checks. Simply move your pacman back to the point where it was not stuck. You accomplish that by inverting the last directions used:

    Code inside Board class

    Change your reaction in case you find a collision: just walk the same distance, backwards.

        if (r3.intersects(r4)) {
            System.out.println("Wall hit, move back");
            pacman.setDx(-pacman.getDx());
            pacman.setDy(-pacman.getDy());
            // Possibly need to call move() here again.
            pacman.move();
            break;
        }
    

    Ugly, but should work.
    Not recommended to coding perfectionists with OCD and heart disease, though.

    2) Redesign

    In this approach, you test the position pacman will occupy before doing any actual moves. If that spot is not into any barrier, then perform the movement for real.

    Code inside Pacman class

    Add this method, so that you can check for collisions against the new bounds.

    public Rectangle getOffsetBounds() {
        return new Rectangle(x + dx, y + dy, width, height);
    }
    

    Code inside Board class

    // Strip the call to pacman.move() prior to this point.
    
    Rectangle r3 = pacman.getOffsetBounds(); // Check against the candidate position.
    
    for (int j = 0; j<barriers.size(); j++) {
        Barrier b = (Barrier) barriers.get(j);
        Rectangle r4 = b.getBounds();
    
        if (r3.intersects(r4)) {
            System.out.println("Wall hit");
            pacman.setDx(0);
            pacman.setDy(0);
            // Quit the loop. It's pointless to check other barriers once we hit one.
            break;
        }
    }
    // Now we're good to move only in case there's no barrier on our way.
    pacman.move();
    

    Particularly I prefer this approach, but it's up to you to pick the best one.