javaswingflashing

Java pong game AI/opponent is not acting as it should


I am coding a Java pong game that is almost complete except that the AI is acting quite oddly I am not sure why it's acting the way it does honestly.

It seems to move up and down rather quickly and has a tendency to appear to teleport and get stuck then suddenly teleport. Main class to start things:

public class Pong {

    Board board = new Board();  
   
   public void frame() {
        JFrame b = new JFrame("Pong");
        b.setSize(905,705);
        b.setLocation(300,60);
        b.setResizable(false);
        b.setVisible(true);
        b.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        
        b.add(board);
   }

   public static  void main(String[] args) {
       Pong start = new Pong();
       start.frame();
   }
}

The board class:

public class Board extends JPanel {

    GamePlay play = new GamePlay(0,0,900,900);

    public Board(){
       // addKeyListener((KeyListener) play.player);
        addKeyListener((KeyListener) play);
        setFocusable(true);

    }

    public void paint(Graphics g) {
            Graphics2D g2 = (Graphics2D) g;
            g2.setColor(Color.black);
            g2.fill(play.a);
            g.setColor(Color.WHITE);
            board(g);
            g2.setColor(Color.white);
            g2.fill(play.opponent.opponent);
            g2.fill(play.ball.ball);

            if(play.playerScore == 5){
                g.setColor(Color.WHITE);    
                g.setFont(new Font("game over",Font.BOLD,20));
                g.drawString("You win!", 300, 300);
                g.drawString("press space bar to restart.", 300, 350);
            }
            if (play.opponentScore == 5){
                g.setColor(Color.WHITE);    
                g.setFont(new Font("game over",Font.BOLD,20));
                g.drawString("You lose!", 300, 300);
                g.drawString("press space bar to restart.", 300, 350);
            }

            repaint();
    }

    public void board(Graphics g) {
            Graphics2D g2d = (Graphics2D) g;
            Stroke stroke1 = new BasicStroke(4f);
            g2d.setColor(Color.white);
            g2d.setStroke(stroke1);
            g2d.drawRect(20, 50, 850, 600);
            g2d.setColor(Color.white);
            float[] dashingPattern2 = {10f, 4f};
            Stroke stroke2 = new BasicStroke(4f, BasicStroke.CAP_BUTT,
            BasicStroke.JOIN_MITER, 1.0f, dashingPattern2, 0.0f);
            g2d.setStroke(stroke2);
            g2d.drawLine(435, 50, 435, 650);
            g.setFont(new Font("arial",Font.PLAIN,30));
            g.drawString(""+play.playerScore, 20, 35);
            g.drawString(""+play.opponentScore, 855, 35);
    }

}

The GamePlay class is more or less just to make things run:

public class GamePlay extends JPanel implements ActionListener,KeyListener {
    
    public int x,y,z,c;
    public int playerScore = 0;
    public int opponentScore = 0;
    boolean over = false;
    Rectangle a;

    Opponent opponent = new Opponent(835,300,15,80);
    Ball ball = new Ball(400,350,20,20);

    public GamePlay(int x,int y,int z,int c){
        this.x = x;
        this.y = y;
        this.z = z;
        this.c = c;
        a = new Rectangle(x, y, z, c);

        timer.start();

    }

    public void collision(){
        opponent.move();

        opponent.getBallPos(ball.ball.y);

        if(ball.ball.intersects(opponent.opponent)){
            ball.right = false;

        }        


        if(ball.ball.intersects(opponent.opponent)){
            ball.right = false;
        }

        if(ball.ball.x >= 862){
            playerScore = playerScore + 1;
        }

        if(ball.ball.x <= 4){
            opponentScore = opponentScore + 1;

        }
    }


    Timer timer = new Timer(5, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            //opponent.move();

            collision();


            if(playerScore ==5 || opponentScore == 5){
                over = true;
                timer.stop();
            }

            //repaint the screen
            if(ball.right == false){
                //collision();
               // opponent.move();

                ball.move();
            //repaint();

            }

            if(ball.right == true){
            //collision();
                //opponent.move();

                ball.move();
            //repaint();

            }

            

        }
    });


    @Override
    public void actionPerformed(ActionEvent arg0) {
        // TODO Auto-generated method stub
        throw new UnsupportedOperationException("Unimplemented method 'actionPerformed'");
    }


    @Override
    public void keyPressed(KeyEvent e) {
        if(e.getKeyCode() == KeyEvent.VK_SPACE) {
            if(over == true){
                timer.start();
                opponentScore = 0;
                playerScore = 0;
                opponent.opponent.x = 835;
                opponent.opponent.y = 300;
                over = false;
                repaint();


            }

       }
  
    }


    @Override
    public void keyReleased(KeyEvent arg0) {

    }


    @Override
    public void keyTyped(KeyEvent arg0) {

    }



}

The balls class (of course it controls the ball):

public class Ball extends JPanel{
    
    public int ballXpos;
    public int ballYpos;
    public int ballWidth;
    public int ballHeight;
    boolean right = false;
    boolean up = true;
    Random random = new Random();

    Rectangle ball;

    public Ball(int ballXpos, int ballYpos, int ballWidth, int ballHeight){
        this.ballXpos = ballXpos;
        this.ballYpos = ballYpos;
        this.ballWidth = ballWidth;
        this.ballWidth = ballHeight;
        up = random.nextBoolean();
        right = random.nextBoolean();
        ball = new Rectangle(ballXpos, ballYpos, ballWidth, ballHeight);

        //move();
    } 

/* to make the ball move properly when hit by paddles have it boolean paddels up means the ball direction means up down moving paddle means ball goes down
 * can be down using boolean true and false but maybe can be done with less lines of code using variebles
 * 
 */
    public void move(){
        if(right == false){
           
            ball.x = ball.x - 1;
        }
        
        if(right == true){
            
            ball.x = ball.x + 1;
            
        }
        if(up == true){
            ball.y = ball.y - 1;
        }

        if(up == false){
            ball.y = ball.y + 1;

        }

        if(ball.x >= 863){
            ball.x = 400;
            up = random.nextBoolean();
            right = random.nextBoolean();

        }

        if(ball.x <= 3){
            ball.x = 400;
            up = random.nextBoolean();
            right = random.nextBoolean();
        }

        if(ball.y <= 38){
            up = false;
        }

        if(ball.y >= 630){
            up = true;
        }
    }
}

And the AI class:

public class Opponent {
public int opponentXpos;
public int opponentYpos;
public int opponentWidth;
public int opponentHeight;
public int ballPos;
Rectangle opponent;

public Opponent(int opponentXpos, int opponentYpos, int opponentWidth, int opponentHeight){
    this.opponentXpos = opponentXpos;
    this.opponentYpos = opponentYpos;
    this.opponentWidth = opponentWidth;
    this.opponentWidth = opponentHeight;
    opponent = new Rectangle(opponentXpos, opponentYpos, opponentWidth, opponentHeight);
}


public void move(){

    if(opponent.y >= 570){

        opponent.y = 570 ;
    }

    if(opponent.y <= 50){

        opponent.y = 50;
    }

    if(opponent.y <= ballPos){
        opponent.y = opponent.y + 1;

    }

    if(opponent.y >= ballPos){
        opponent.y = opponent.y = - 1;

    }


    if(opponent.y == ballPos){
        opponent.y = opponent.y;

    }


}

public void getBallPos(int ball){
    this.ballPos = ball;

}

}

I have tried to move around where I call for the opponents move method, I have negated the issue somewhat but cannot fix it completely.

I expect of course the paddle to move smoothly like the ball does and try to follow the ball rather than moving up and down the way it does.

Maybe I need to implement a separate action listener for the AI rather than using the one for the ball.


Solution

  • Issue is solved.

    Int the opponent class change the movement into this.

    public void move(int ballY){
    
        if(opponent.y >= 566){
            opponent.y = 566 ;
        }
    
        if(opponent.y <= 50){
            opponent.y = 50;
        }
    
        if (opponent.y  < ballY) {
            down = true;
            up = false;
            opponent.y += 3;
    
        } else if (opponent.y  > ballY) {
            down = false;
            up = true;
            opponent.y -= 3;
        }
    

    In the GamePlay class change it like this adding another timer.

    Timer timer2 = new Timer(17, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            opponent.move(ball.ball.y);
            collision();
    
        }
    });