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

问题描述 投票:0回答:1

我正在制作小行星游戏。每隔一段时间,就需要产生一颗小行星并在屏幕上飞行。由于某种原因,当创建的小行星超过1个时,屏幕会出现故障。如果最大化屏幕,您将能够看到故障。我尝试使用油漆而不是paintComponent。我也尝试过扩展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++;

        }
    }
}

下面的课程设置了小行星

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;
    }
}```
java swing user-interface animation paint
1个回答
1
投票

我可以看到的最大问题是,您使Asteroids类扩展了JPanel,使其重量比原本应的重得多,并且不易显示多个对象,并且它们之间难以轻松,良好地交互。

我建议您:

  • 将小行星设为非成分逻辑类,
    • 知道如何通过给其public void draw(Graphics2D g2)方法进行绘制的人
    • 知道如何根据游戏循环的滴答声移动自己的人
  • 仅创建一个JPanel以绘制整个动画
  • 给这个JPanel一个小行星对象的集合,例如在ArrayList中说
  • 在此JPanel的paintComponent中,遍历集合中的所有小行星,调用每个小行星的draw(...)方法
  • 使用Swing计时器以Swing线程安全且可控制的方式驱动整个动画。在此计时器的actionPerformed中,告诉每个小行星移动,然后在图形JPanel上调用repaint()
  • 不要在循环内调用.repaint(),而要在循环完成后调用
  • 从您的形状中创建一个小的BufferedImage精灵,并将其绘制为小行星

一个简单的例子,说明了我的意思:

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);
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.