Java Pong游戏AI对手行为异常问题求助
Java Pong游戏AI对手异常修复方案
问题概述
开发Java Pong游戏时,AI对手存在行为异常:快速上下移动、频繁瞬移、卡顿后突然瞬移,无法平滑跟随球的位置移动。
核心问题分析
- Opponent类move方法逻辑错误:存在笔误导致AI直接瞬移到顶部,且跟随逻辑冗余;
- 球位置更新顺序错误:AI移动时使用的是上一帧的球位置,导致跟随延迟;
- 无限重绘导致渲染异常:Board类paint方法中调用
repaint(),引发无限重绘循环; - Timer逻辑冗余:GamePlay中Timer的动作监听存在重复的球移动判断,且AI移动触发时机混乱;
- Ball类构造方法参数错误:误将ballHeight赋值给ballWidth,导致球的尺寸异常。
具体修复步骤
1. 修复Opponent类move方法
修正笔误,简化跟随逻辑,确保AI以自身中心为目标平滑跟随球:
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.opponentHeight = opponentHeight; // 修复参数赋值错误 opponent = new Rectangle(opponentXpos, opponentYpos, opponentWidth, opponentHeight); } public void move() { // 限制AI在游戏区域内 if (opponent.y >= 570) { opponent.y = 570; } if (opponent.y <= 50) { opponent.y = 50; } // 以AI paddle中心为目标,平滑跟随球的Y轴位置 int targetY = ballPos - opponent.height / 2; if (opponent.y < targetY) { opponent.y += 1; } else if (opponent.y > targetY) { opponent.y -= 1; } } public void getBallPos(int ball) { this.ballPos = ball; } }
2. 调整GamePlay中AI逻辑顺序
先更新球位置给AI,再执行AI移动,确保使用最新的球位置,同时简化Timer逻辑:
public class GamePlay extends JPanel implements KeyListener { 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.a = new Rectangle(x, y, z, c); timer.start(); } // 重命名并简化碰撞检测方法 public void checkCollision() { if (ball.ball.intersects(opponent.opponent)) { ball.right = false; } if (ball.ball.x >= 862) { playerScore++; resetBall(); } if (ball.ball.x <= 4) { opponentScore++; resetBall(); } } // 重置球的位置和方向 private void resetBall() { ball.ball.x = 400; ball.ball.y = 350; ball.up = ball.random.nextBoolean(); ball.right = ball.random.nextBoolean(); } Timer timer = new Timer(16, new ActionListener() { // 调整为16ms约60帧,提升稳定性 @Override public void actionPerformed(ActionEvent e) { // 先同步最新球位置给AI opponent.getBallPos(ball.ball.y); // 执行AI移动 opponent.move(); // 执行球移动 ball.move(); // 检测碰撞 checkCollision(); if (playerScore == 5 || opponentScore == 5) { over = true; timer.stop(); } // 触发重绘 repaint(); } }); @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_SPACE && over) { timer.start(); opponentScore = 0; playerScore = 0; opponent.opponent.x = 835; opponent.opponent.y = 300; over = false; resetBall(); } } @Override public void keyReleased(KeyEvent arg0) {} @Override public void keyTyped(KeyEvent arg0) {} }
3. 修复Board类的无限重绘问题
移除paint方法中的repaint()调用,改用Timer触发重绘,同时调用父类paint方法清除画布:
import java.awt.*; import javax.swing.*; public class Board extends JPanel { GamePlay play = new GamePlay(0, 0, 900, 900); public Board() { addKeyListener(play); setFocusable(true); } @Override public void paint(Graphics g) { super.paint(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("Arial", 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("Arial", Font.BOLD, 20)); g.drawString("You lose!", 300, 300); g.drawString("press space bar to restart.", 300, 350); } } 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); } }
4. 修复Ball类构造方法参数错误
修正ballHeight的赋值错误,确保球的尺寸正确:
import java.awt.*; import java.util.Random; import javax.swing.*; 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.ballHeight = ballHeight; // 修复赋值错误 up = random.nextBoolean(); right = random.nextBoolean(); ball = new Rectangle(ballXpos, ballYpos, ballWidth, ballHeight); } 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){ resetBallPosition(); } if(ball.x <= 3){ resetBallPosition(); } if(ball.y <= 38){ up = false; } if(ball.y >= 630){ up = true; } } private void resetBallPosition() { ball.x = 400; ball.y = 350; up = random.nextBoolean(); right = random.nextBoolean(); } }
额外优化建议
- 可给AI添加动态速度调整逻辑,比如球离AI越远,移动步长越大,提升AI真实感;
- 统一使用
Timer控制游戏帧率,避免过高帧率导致的性能消耗; - 为玩家添加 paddle 控制逻辑,完善游戏完整性。
内容的提问来源于stack exchange,提问作者Jon doe
相关产品推荐
相关产品推荐

