Java 为什么在绘制多个对象时JFrame会出现故障?

Java 为什么在绘制多个对象时JFrame会出现故障?,java,swing,user-interface,animation,paint,Java,Swing,User Interface,Animation,Paint,我正在做一个小行星游戏。每隔一段时间就需要生成一颗小行星,并在屏幕上飞行。由于某种原因,当创建超过1颗小行星时,屏幕会出现小故障。如果您最大化屏幕,您将能够看到故障。我试着用油漆代替油漆组件。我也尝试过扩展JFrame而不是JPanel,但这只会让事情变得更糟。下面的类设置屏幕并处理游戏循环 public class Game extends JPanel { static ArrayList<Asteroids> rocks = new ArrayList<Aster

我正在做一个小行星游戏。每隔一段时间就需要生成一颗小行星,并在屏幕上飞行。由于某种原因,当创建超过1颗小行星时,屏幕会出现小故障。如果您最大化屏幕,您将能够看到故障。我试着用油漆代替油漆组件。我也尝试过扩展JFrame而不是JPanel,但这只会让事情变得更糟。下面的类设置屏幕并处理游戏循环

public class Game extends JPanel {
    static ArrayList<Asteroids> rocks = new ArrayList<Asteroids>();

    //This variable determines whether the game should keep running
    static boolean running = true;

    //Counter to access arraylist
    static int counter = 0;

    public static void main(String[] args) throws InterruptedException {
        //Creating the window
        JFrame frame = new JFrame("Asteroid Game");
        frame.getContentPane().setBackground(Color.BLACK);
        frame.setSize(1100, 1000);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);  

        Asteroids a = new Asteroids();
        frame.add(a);

        //Game loop
        while(running) {
            if(counter % 4 == 0) {
                rocks.add(new Asteroids());
                frame.add(rocks.get(rocks.size() - 1));
            }

            for(int i = 0; i < rocks.size(); i++) {
                rocks.get(i).repaint();
                rocks.get(i).move();
                if(!rocks.get(i).isPosFine()) {
                    rocks.remove(i);
                    i--;
                }
            }



            Thread.sleep(17);
            counter++;

        }
    }
}
公共类游戏扩展JPanel{
静态ArrayList rocks=新ArrayList();
//此变量决定游戏是否应继续运行
静态布尔运行=真;
//访问arraylist的计数器
静态整数计数器=0;
公共静态void main(字符串[]args)引发InterruptedException{
//创建窗口
JFrame=新JFrame(“小行星游戏”);
frame.getContentPane().setBackground(颜色:黑色);
框架设置尺寸(11001000);
frame.setLocationRelativeTo(空);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
小行星a=新的小行星();
框架.添加(a);
//游戏循环
(跑步时){
如果(计数器%4==0){
添加(新小行星());
frame.add(rocks.get(rocks.size()-1));
}
对于(int i=0;i
下面的类设置了小行星

public class Asteroids extends JPanel {
    //These arrays store the coordinates of the asteroid
    private int[] xPos = new int[8];
    private int[] yPos = new int[8];

    //Determines whether asteroid should be generated from top or bottom
    private int[] yGen = {-100, 1100};

    //Determines the direction the asteroid shold go
    int genLevel;

    /**
     * @param g Graphics
     * This method paints the asteroid
     */
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D r = (Graphics2D)g;
        r.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        r.setColor(Color.decode("#52575D"));
        r.fillPolygon(xPos, yPos, 8);
    }

    /**
     * This constructor sets up the asteroid location points
     */
    public Asteroids() {
        int x = (int)(Math.random() * (700 - 1) + 100);
        int y = yGen[(int)(Math.random() * (1 + 1 - 0))];
        updateAsteroids(x, y);
        genLevel = y;
        System.out.println("Created!");
    }

    /**
     * @param x int
     * @param y int
     * This method generates the asteroid based on the points passed in
     */
    public void updateAsteroids(int x, int y) {
        xPos[0] = x;
        xPos[1] = x + 20;
        xPos[2] = x + 40;
        xPos[3] = x + 35;
        xPos[4] = x + 40;
        xPos[5] = x + 4;
        xPos[6] = x - 16;
        xPos[7] = x - 20;

        yPos[0] = y;
        yPos[1] = y + 7;
        yPos[2] = y + 20;
        yPos[3] = y + 40;
        yPos[4] = y + 80;
        yPos[5] = y + 70;
        yPos[6] = y + 40;
        yPos[7] = y;
    }

    /**
     * This moves the asteroid
     */
    public void move() {
        int moveSpeedx = (int)(Math.random() * (10 - 1) + 1);
        int moveSpeedy = (int)(Math.random() * (10 - 1) + 1);
        for(int i = 0; i < 8; i++) {
            if(genLevel > 0) {
                xPos[i] -= moveSpeedx;
                yPos[i] -= moveSpeedy;
            }
            else {
                xPos[i] += moveSpeedx;
                yPos[i] += moveSpeedy;
            }

        }
    }

    /**
     * @return if the asteroid should be kept on the screen or not 
     */
    public boolean isPosFine() {
        for(int i = 0; i < 8; i++) {
            if(xPos[i] > 1250 || xPos[i] < -150)
                return false;
            if(yPos[i] > 1250 || yPos[i] < - 150)
                return false;
        }
        return true;
    }
}```
公共类小行星扩展了JPanel{
//这些阵列存储小行星的坐标
私有int[]xPos=新int[8];
私有int[]yPos=新int[8];
//确定是从顶部还是从底部生成小行星
私有int[]yGen={-1001100};
//确定小行星应该运行的方向
int-genLevel;
/**
*@param g图形
*这种方法绘制小行星
*/
@凌驾
公共组件(图形g){
超级组件(g);
图形2d r=(图形2d)g;
r、 setRenderingHint(RenderingHits.KEY\u ANTIALIASING,RenderingHits.VALUE\u ANTIALIAS\u ON);
r、 setColor(Color.decode(#52575D));
r、 填充多边形(XPO、YPO、8);
}
/**
*此构造函数设置小行星位置点
*/
公共小行星(){
intx=(int)(Math.random()*(700-1)+100);
int y=yGen[(int)(Math.random()*(1+1-0))];
UpdatesErroroids(x,y);
genLevel=y;
System.out.println(“已创建!”);
}
/**
*@param x int
*@param y int
*此方法基于传入的点生成小行星
*/
public void updatesErroroids(int x,int y){
xPos[0]=x;
xPos[1]=x+20;
xPos[2]=x+40;
xPos[3]=x+35;
xPos[4]=x+40;
xPos[5]=x+4;
xPos[6]=x-16;
xPos[7]=x-20;
yPos[0]=y;
yPos[1]=y+7;
yPos[2]=y+20;
yPos[3]=y+40;
yPos[4]=y+80;
yPos[5]=y+70;
yPos[6]=y+40;
yPos[7]=y;
}
/**
*这会移动小行星
*/
公开作废动议(){
int moveSpeedx=(int)(Math.random()*(10-1)+1);
int=(int)(Math.random()*(10-1)+1);
对于(int i=0;i<8;i++){
如果(级别>0){
xPos[i]=moveSpeedx;
yPos[i]=1;
}
否则{
xPos[i]+=moveSpeedx;
yPos[i]+=1;
}
}
}
/**
*@return是否应将小行星保留在屏幕上
*/
公共布尔值isPosFine(){
对于(int i=0;i<8;i++){
如果(xPos[i]>1250 | | xPos[i]<-150)
返回false;
如果(yPos[i]>1250 | | yPos[i]<-150)
返回false;
}
返回true;
}
}```

我所能看到的最大问题是,你让你的小行星等级扩展到了JPanel,使它的重量比应该的要重得多,并且使不止一个小行星很难显示出来,它们也很难很好、很容易地相互作用

我建议你:

  • 使小行星成为非组件逻辑类,
    • 一个知道如何绘制自己的人,给它一个
      公共虚空绘制(Graphics2D g2)
      方法
    • 知道如何根据游戏循环的滴答声移动自己的人
  • 创建一个JPanel仅用于绘制整个动画
  • 给这个JPanel一个小行星物体的集合,比如说在ArrayList中
  • 在这个JPanel的paintComponent中,循环遍历集合中的所有小行星,调用每个小行星的
    draw(…)
    方法
  • 使用Swing计时器以Swing线程安全可控的方式驱动整个动画。在此计时器执行的操作中,告诉每个小行星移动,然后在图形JPanel上调用
    repaint()
  • 不要在循环中调用
    .repaint()
    ,而是在循环完成后调用
  • 从您的形状创建一个小的BuffereImage精灵,并将其绘制为小行星
一个简单的例子说明了我的意思:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;

@SuppressWarnings("serial")
public class Game2 extends JPanel {
    private static final int PREF_W = 1000;
    private static final int PREF_H = 800;
    private static final int TIMER_DELAY = 20;
    private List<Asteroid2> asteroids = new ArrayList<>();

    public Game2() {
        setBackground(Color.BLACK);
        int rows = 5;
        int cols = 5;
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                Asteroid2 asteroid = new Asteroid2();
                asteroid.setX(j * (PREF_W / cols));
                asteroid.setY(i * (PREF_H / rows));
                asteroids.add(asteroid);
            }
        }
        new Timer(TIMER_DELAY, e -> {
            for (Asteroid2 asteroid2 : asteroids) {
                asteroid2.move();
            }
            repaint();
        }).start();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        for (Asteroid2 asteroid : asteroids) {
            asteroid.draw(g);
        }
    }

    @Override
    public Dimension getPreferredSize() {
        if (isPreferredSizeSet()) {
            return super.getPreferredSize();
        }
        return new Dimension(PREF_W, PREF_H);
    }

    private static void createAndShowGui() {
        Game2 mainPanel = new Game2();

        JFrame frame = new JFrame("Game2");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}
class Asteroid2 {
    private static final int[] POLY_X = { 20, 40, 60, 55, 60, 24, 4, 0 };
    private static final int[] POLY_Y = { 0, 7, 20, 40, 80, 70, 40, 0 };
    private static final Color ASTEROID_COLOR = Color.decode("#52575D");
    private Image image;
    private int x;
    private int y;

    public Asteroid2() {
        Polygon poly = new Polygon(POLY_X, POLY_Y, POLY_X.length);
        image = new BufferedImage(60, 80, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = (Graphics2D) image.getGraphics();
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setColor(ASTEROID_COLOR);
        g2.fill(poly);
        g2.dispose();
    }

    public void move() {
        x++;
        y++;
    }

    public void setX(int x) {
        this.x = x;
    }

    public void setY(int y) {
        this.y = y;
    }

    public void draw(Graphics g) {
        if (image != null) {
            g.drawImage(image, x - 20, y, null);
        }
    }
}