Search code examples
javaswingcollision-detectiongame-physicscustom-painting

Java: Pacman collision detection with wall


I have been trying my best to work out collision detection between Pacman and the walls, however my implementation doesn't seem to work as correctly

  • Does collision detection work at all? Yes
  • Does it behave correctly? No
  • How is it currently behaving? When you hit a wall it stops Pacman from moving which is okay, however any new key presses to move only changes the images axis (up, right, down or left depending on key press), it will not move the Pacman beyond its current location after hitting the wall.

Any assistance would be greatly appreciated. thanks in advance

GamePanel.java

package pacman;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JPanel;

public class GamePanel extends JPanel implements Runnable{
    private Thread animator;
    private boolean isRunning;  
    private Map map = new Map();


    public GamePanel(){
        this.setBackground(Color.BLACK);
        this.setDoubleBuffered(true);
        addKeyListener(new TAdapter());
        setFocusable(true);
    }

    @Override
    public void run() {
        isRunning = true;
        System.out.println("Is running? "+isRunning);
        long startTime, timeDiff, sleepTime; 
        startTime = System.currentTimeMillis();

        while(isRunning){
            repaint();
            gameUpdate();


             timeDiff = System.currentTimeMillis() -  startTime;
             sleepTime = 5 - timeDiff;

            try{
                Thread.sleep(sleepTime);
            }
            catch(InterruptedException ex){
                System.exit(0);
            }
            startTime = System.currentTimeMillis();

        }   
//      gameOver(); not implemented yet, will focus on this when I have some basic animation and the game loop working to satisfaction.
    }


    @Override
    public void addNotify(){
        super.addNotify();
        startGame();
    }

    public void startGame(){        
        if(animator == null || !isRunning){
            animator = new Thread(this);
            animator.start();           
        }
    } //end of StartGame method

    public void gameUpdate(){   
        map.getPlayer().move();     
        checkCollision();
    } //implementation of ingame updates such as pacman getting killed.


    public void checkCollision(){ //this is where I officially set collision up
        for(int i = 0; i < map.tiles.length; i++){
            for(int j = 0; j < map.tiles.length; j++){
                if(map.tiles[i][j] != null){
                    if(map.getPlayer().getPlayerBox().intersects(map.tiles[i][j].getR())){
                        map.getPlayer().setColliding(true);
                        System.out.println("OWW"+map.tiles[i][j].getR().getLocation());

                    }               
                }                           
            }               
        }
    }
    public void paintComponent(Graphics g){ 
        Graphics2D g2d = (Graphics2D) g;
        super.paintComponent(g);
        if(isRunning){
            drawDot(g2d);
            drawPlayer(g2d);
            map.drawMap(g2d);
        }

        Toolkit.getDefaultToolkit().sync();
        g.dispose();

    }   

    public void drawDot(Graphics2D g){
        g.setColor(Color.GREEN);
        for(int x= 0; x < 400; x++){
            for(int y = 0; y < 400; y++){
                g.drawRect(x * 20, y * 20, 1, 1);
            }
        }

    }




    public void drawPlayer(Graphics2D g){       
        g.drawImage(map.getPlayer().getImage(), map.getPlayer().getX(),map.getPlayer().getY(), this); 
    }

private class TAdapter extends KeyAdapter{      

        @Override
        public void keyPressed(KeyEvent e) {            
            map.getPlayer().keyPressed(e);  
        }
        @Override

        public void keyReleased(KeyEvent e) {
            map.getPlayer().keyReleased(e);
        }
    }
}

Player.java

package pacman;


import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;

import javax.swing.ImageIcon;

public class Player extends Commons{
    private int dx, dy;
    private int speed = 1;
    private static int playerWidth = 52; //these figures seem off, but that is because the image is not designed correctly or the pacman image is not obeying the logic of not going passed the frame size.
    private static int playerHeight = 82;
    private Rectangle playerBox;
    private Image playerImg  = new ImageIcon(Player.class.getResource("Pacman.png")).getImage();
    private Image playerImgUp  = new ImageIcon(Player.class.getResource("PacmanUp.png")).getImage();
    private Image playerImgLeft  = new ImageIcon(Player.class.getResource("PacmanLeft.png")).getImage();
    private Image playerImgDown  = new ImageIcon(Player.class.getResource("PacmanDown.png")).getImage();

    private boolean isColliding = false;



    public Player(){
        this.setX(320);
        this.setY(280);
        playerBox = new Rectangle(this.getX(), this.getY(),40,40);
    }

    public void setSpeed(int sp){
        speed = sp;
    }

    public Image getImage(){
        if(dy == 1){            
            return playerImgDown;
        } 
        else if(dy == -1){
            return playerImgUp;
        }
        else if(dx == -1){
            return playerImgLeft;
        }
        else 
            return playerImg;       
    }//Responsible for displaying pacman image based on direction of pacman's movement.



    void move(){
        int x = this.getX();
        int y = this.getY();
        /*
         * This is the part where I am trying to implement some degree of logic to stop it from moving
         */
        if(isColliding == false){
            this.setX(x += dx);
            playerBox.setLocation(x, y);
            this.setY(y += dy);
        }else if(isColliding == true){
            this.setColliding(false);
            this.setX(this.getX());
            this.setY(this.getY());

        }

        if (this.getX() <= 1) {
            this.setX(1);
        }
        if (this.getX() >= 400 - playerWidth) {
            this.setX(400 - playerWidth);
        }
        if (this.getY() <= 2) {
            this.setY(2);
        }
        if (this.getY() >= 400 - playerHeight ) {
            this.setY(400 - playerHeight);
        }
    }//Most simplist form of collision detection, stops pacman from leaving the JFrame

    public void keyPressed(KeyEvent e) {
        int key = e.getKeyCode();


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

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

        if(key == KeyEvent.VK_ESCAPE){
            System.exit(0);
        }   
    }



    public void keyReleased(KeyEvent e) {
        int key = e.getKeyCode();
        if(key == KeyEvent.VK_LEFT){
            dy = 0;     
        }
        if(key == KeyEvent.VK_RIGHT){
            dy = 0;         
        }
        if(key == KeyEvent.VK_UP){
            dx = 0;         
        }
        if(key == KeyEvent.VK_DOWN){
            dx = 0;         
        }       
    }//end of key release



    public Rectangle getPlayerBox() {
        return playerBox;
    }




    public void setPlayerBox(Rectangle playerBox) {
        this.playerBox = playerBox;
    }


    public boolean isColliding() {
        return isColliding;
    }


    public void setColliding(boolean isColliding) {
        this.isColliding = isColliding;
    }


}// end of class

Solution

  • It looks like what happens is the player enters a tile which causes collision and isColliding is set to true. The next iteration of the game update calls movement code which checks the player's isColliding boolean. That condition is true and no movement occurs. Next, collision is checked and we haven't moved out of the tile causing the collision so we are stuck in this tile.

    I suggest when collision happens to move the player just out of the colliding tile.

    We know which direction the player is moving so we can use the inverse direction (multiply dx and dy by -1) to move the player out of the colliding tile.