Java游戏开发求助:敌方红色方块repaint功能失效排查
问题分析与解决方案
嘿,我帮你找到了问题的根源,咱们一步步来解决:
核心问题:操作了错误的组件实例
你在java.util.Timer的任务里每次都new boxface(),这意味着你每次都创建了一个全新的游戏组件实例,而不是操作界面上正在显示的那个!所以你调用enemyDown()和repaint()都是在对一个看不见的组件生效,界面上的敌方方块自然不会动。
另外,java.util.Timer的任务是在非EDT(事件调度线程)执行的,直接操作Swing组件会有线程安全问题,咱们换成Swing专用的javax.swing.Timer会更稳妥。
具体修改步骤
- 替换Timer类型:把
java.util.Timer换成javax.swing.Timer,它的任务会自动在EDT线程执行,避免Swing组件的线程安全问题。 - 获取界面上的真实实例:在main方法里创建boxface实例后保存引用,让Timer任务直接操作这个实例,而不是每次新建。
- 修正敌方方块的重置逻辑:把判断
enemy.getBounds().y == 400调整为y >= 400,避免因为意外的数值偏差导致无法触发重置(比如如果y的步长不小心修改)。 - 成员变量初始化优化:把player、enemy这些矩形对象的初始化放到构造方法里,确保每次操作的都是当前组件的实例对象。
修改后的完整代码
import javax.swing.*; import java.awt.*; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class boxface extends JComponent implements KeyListener { private int x = 0, y = 0; private Rectangle player; private Rectangle bg; private Rectangle enemy; private int x2; public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_RIGHT) moveRight(); else if (e.getKeyCode() == KeyEvent.VK_LEFT) moveLeft(); } public void keyReleased(KeyEvent e) {} public void keyTyped(KeyEvent e) {} public boxface() { // 在构造方法里初始化所有矩形,确保属于当前组件实例 player = new Rectangle(x, 400, 50, 50); bg = new Rectangle(0, 0, 700, 750); x2 = ((int) (Math.random() * 3)) * 50; enemy = new Rectangle(x2, y, 50, 50); addKeyListener(this); setFocusable(true); setFocusTraversalKeysEnabled(false); } public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D) g; g2.setColor(Color.BLACK); g2.fill(bg); g2.setColor(Color.WHITE); g2.fill(player); g2.setColor(Color.RED); g2.fill(enemy); } public void moveLeft() { if (x > 0) { x -= 50; player.setLocation(x, 400); repaint(); } } public void moveRight() { if (x < 100) { x += 50; player.setLocation(x, 400); repaint(); } } public void enemyDown() { // 调整为>=400,避免数值偏差导致无法重置 if (y >= 400) { x2 = ((int) (Math.random() * 3)) * 50; y = 0; enemy.setLocation(x2, y); } else { y += 50; enemy.setLocation(x2, y); } repaint(); // 把repaint移到方法末尾,确保无论分支都会重绘 } public void intersector() { if (enemy.intersects(player)) { // 添加游戏结束提示,替代直接关闭Timer JOptionPane.showMessageDialog(this, "游戏结束!"); System.exit(0); } } public static void main(String[] args) { javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { JFrame f = new JFrame(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setBounds(400, 200, 156, 479); f.setMinimumSize(new Dimension(156, 0)); f.setResizable(false); // 创建并保存界面上的boxface实例 boxface gamePanel = new boxface(); f.getContentPane().add(gamePanel); f.setVisible(true); // 使用javax.swing.Timer,任务在EDT执行 Timer timer = new Timer(1000, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { gamePanel.enemyDown(); gamePanel.intersector(); } }); timer.start(); } }); } }
关键修改点解释
- Timer替换:用
javax.swing.Timer代替java.util.Timer,确保所有组件更新操作都在EDT线程,避免Swing的线程安全问题。 - 实例复用:在main方法中创建
gamePanel实例并保存,Timer任务直接调用这个实例的方法,操作的就是界面上显示的组件。 - repaint位置调整:把
repaint()移到enemyDown()方法的末尾,无论敌方方块是下落还是重置,都会触发重绘。 - 初始化优化:把矩形对象的初始化放到构造方法里,确保每个组件实例都有自己的player和enemy对象,不会出现引用混乱。
现在运行修改后的代码,敌方方块应该会正常下落,到达底部后重置位置,并且碰撞检测也能正常工作啦!
内容的提问来源于stack exchange,提问作者Paulo Santos
相关产品推荐
相关产品推荐

