You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java游戏开发求助:敌方红色方块repaint功能失效排查

问题分析与解决方案

嘿,我帮你找到了问题的根源,咱们一步步来解决:

核心问题:操作了错误的组件实例

你在java.util.Timer的任务里每次都new boxface(),这意味着你每次都创建了一个全新的游戏组件实例,而不是操作界面上正在显示的那个!所以你调用enemyDown()和repaint()都是在对一个看不见的组件生效,界面上的敌方方块自然不会动。

另外,java.util.Timer的任务是在非EDT(事件调度线程)执行的,直接操作Swing组件会有线程安全问题,咱们换成Swing专用的javax.swing.Timer会更稳妥。

具体修改步骤

  1. 替换Timer类型:把java.util.Timer换成javax.swing.Timer,它的任务会自动在EDT线程执行,避免Swing组件的线程安全问题。
  2. 获取界面上的真实实例:在main方法里创建boxface实例后保存引用,让Timer任务直接操作这个实例,而不是每次新建。
  3. 修正敌方方块的重置逻辑:把判断enemy.getBounds().y == 400调整为y >= 400,避免因为意外的数值偏差导致无法触发重置(比如如果y的步长不小心修改)。
  4. 成员变量初始化优化:把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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:05:18