-3

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.

Ben the Coder
  • 539
  • 2
  • 5
  • 21
Jon doe
  • 13
  • 3
  • 3
    1) Don't invoke repaint() in the paint() method. The `repaint()` method should be invoked in the `Timer` code. 2) Don't overridie paint(). Custom painting is done by overriding `paintComponent(...)`. Also make sure you invoke super.paintComponent(...) as the first statement to make sure the background is cleared before doing your custom painting. – camickr Mar 07 '23 at 15:23
  • @camickr this would solve the issue? – Jon doe Mar 07 '23 at 15:28
  • There may be other problems but this is the proper way to do custom painting and use animation, so these are the first things to fix. Read the [Swing tutorial](https://docs.oracle.com/javase/tutorial/uiswing/TOC.html). There are section on `Performing Custom Painting` and `How to Use Swing Timers`. – camickr Mar 07 '23 at 15:32
  • Please post a [minimal reproducible example](https://stackoverflow.com/help/MINIMAL-REPRODUCIBLE-EXAMPLE). – Ben the Coder Mar 07 '23 at 15:37
  • Invoking repaint() does nothing in Timer code @camickr – Jon doe Mar 07 '23 at 15:38
  • @BentheCoder I have done that. – Jon doe Mar 07 '23 at 15:42
  • *Invoking repaint() does nothing in Timer code* - I see where you invoke ball.move(). Where do you invoke AI.move()? See: https://stackoverflow.com/a/54028681/131872 for an [mre] that demonstrates animation using a Timer and custom painting. – camickr Mar 07 '23 at 16:04
  • @camickr I invoke the AI(opponent class) In that code example in the collison class but invoking it in the action timer has the same effect – Jon doe Mar 07 '23 at 16:15
  • This [Stack Overflow answer](https://stackoverflow.com/questions/66732003/how-to-add-smooth-movement-to-my-java-game/66734544#66734544) about another Pong game might help explain some of the problems with your code. – Gilbert Le Blanc Mar 07 '23 at 16:39
  • @GilbertLeBlanc I see what you mean but not the solution to my problem. – Jon doe Mar 07 '23 at 16:54

1 Answers1

0

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();

    }
});
Jon doe
  • 13
  • 3