简体   繁体   English

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

[英]Why does the JFrame glitch when multiple objects are painted?

I am making an asteroid game.我正在制作一个小行星游戏。 Every so often an asteroid needs to be generated and fly across the screen.每隔一段时间,需要生成一个小行星并飞过屏幕。 For some reason when more then 1 asteroid is created, the screen glitches out.出于某种原因,当创建超过 1 个小行星时,屏幕会出现故障。 If you maximize the screen you will be able to see the glitching.如果您最大化屏幕,您将能够看到故障。 I have tried using paint instead of paintComponent.我试过用油漆代替paintComponent。 I have also tried extending JFrame instead of JPanel but that just makes it worse.我也试过扩展 JFrame 而不是 JPanel 但这只会让情况变得更糟。 The class below sets up the screen and handles the game loop下面的类设置屏幕并处理游戏循环

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++;

        }
    }
}

The class below sets up the asteroids下面的课程设置小行星

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;
    }
}```

Your biggest problem that I can see is that you made your Asteroids class extend JPanel, making it much heavier weight than it should be, and making it difficult for more than one to show and for them to interact well and easily.我可以看到你的最大问题是你让你的小行星类扩展了 JPanel,使它的重量比它应该的要重得多,并且使得多个显示和它们之间很难很好地和轻松地交互。

I recommend that you:我建议你:

  • Make Asteroid a non-component logical class,使 Asteroid 成为非组件逻辑类,
    • one that knows how to draw itself by giving it a public void draw(Graphics2D g2) method一个知道如何通过给它一个public void draw(Graphics2D g2)方法来绘制自己的人
    • one that knows how to move itself in response to your game loop's tick一种知道如何根据游戏循环的滴答声移动自己的人
  • Create one JPanel just for drawing the entire animation创建一个 JPanel 只是为了绘制整个动画
  • Give this JPanel a collection of Asteroid objects, say in an ArrayList给这个 JPanel 一个 Asteroid 对象的集合,比如在一个 ArrayList 中
  • In this JPanel's paintComponent, loop through all Asteroids in the collection, calling each one's draw(...) method在这个JPanel的paintComponent中,循环遍历集合中的所有Asteroids,调用每一个的draw(...)方法
  • Drive the whole animation in a Swing thread-safe and controllable way using a Swing Timer.使用 Swing 计时器以 Swing 线程安全且可控的方式驱动整个动画。 In this timer's actionPerformed, tell each asteroid to move, and then call repaint() on the drawing JPanel在这个定时器的actionPerformed中,告诉每个小行星移动,然后在画图JPanel上调用repaint()
  • Don't call .repaint() within the loop, but rather after the loop is finished不要在循环内调用.repaint() ,而是在循环完成后调用
  • Create a small BufferedImage sprite from your Shapes, and draw those as the asteroid从你的形状创建一个小的 BufferedImage 精灵,并将它们绘制为小行星

A simple example illustrating what I mean:一个简单的例子说明了我的意思:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM