1

我有两个 JPanel。一个面板在 0,0 处绘制了一个 100x100 的矩形。另一个在 100、100 处绘制了一个 100x100 的矩形。我的问题是,当两个 JPanel 都绘制在 JFrame 的内容窗格上时,一个 JPanel(最后一个绘制的)覆盖另一个,隐藏了它的图形。下面是绘制两个矩形的过于简化的代码以及我尝试过的东西。

package playground;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Playground{

    public Playground(){
        JFrame frame = new JFrame("My Frame");
        frame.setSize(400, 400);

        JPanel backPanel = new JPanel(){;

                @Override
                public void paintComponent(Graphics g){
                    super.paintComponent(g);
                    Graphics2D g2 = (Graphics2D)g;
                    Rectangle2D rect = new Rectangle2D.Double(0, 0, 100, 100);
                    g2.draw(rect);
                }
        };
        JPanel frontPanel = new JPanel(){

            @Override
            public void paintComponent(Graphics g){
                super.paintComponent(g);
                Graphics2D g2 = (Graphics2D)g;
                Rectangle2D rect = new Rectangle2D.Double(150, 150, 100, 100);
                g2.draw(rect);
            }
        }; 
        frontPanel.setOpaque(true); //Does nothing
        frontPanel.setBackground(new Color(0, 0, 0, 0)); //Does nothing
        frontPanel.setForeground(new Color(0, 0, 0, 0)); //Erases the rectangle drawn


        frame.getContentPane().add(backPanel);
        frame.getContentPane().add(frontPanel);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    public static void main(String[] args){
        System.out.println("Hello World");
        new Playground();
    }

}

如果有人关心我为什么要这样做,

我正在创建游戏突破。我是一名新手程序员,对博弈论一无所知。所以我决定避免大量渲染和缓冲的最聪明的方法是拥有四个 JPanel。背面的静态 JPanel 上绘制了图像(有趣的背景图像)。一个 JPanel,上面画着桨。一个 JPanel,上面画着砖块。还有一个 JPanel,上面画了一个球。我的理由是,如果桨没有被移动,我不必重新绘制桨,背景和没有被击中的砖块。如果一个砖块被击中,我将更新一个砖块数组列表并在相应的 JPanel 上调用 repaint。

4

1 回答 1

4

我是一名新手程序员,对博弈论一无所知。

好的,我们可以使用它。

所以我决定避免大量渲染和缓冲的最聪明的方法是拥有四个 JPanel。

你只是不必要地复杂了你的程序。

将 JPanel 视为画布。您想绘制整个 Breakout 游戏;一块 JPanel 画布上的积木、桨和球。不用担心,如果需要,您将能够以足够快的速度重绘整个画布,以达到每秒 60 帧的速度。

这样做的方法是创建一个 Brick 类、一个 Paddle 类和一个 Ball 类。您创建一个游戏模型类,其中包含一个 Paddle 类实例、一个 Ball 类实例和一个 Brick 类实例列表。

Brick 类将有字段来确定它在墙上的位置、球与砖碰撞时得分的数量、砖的颜色以及知道如何绘制一块砖的绘制方法。

球类将具有确定其 x、y 位置、方向、速度和知道如何绘制球的绘制方法的字段。

Paddle 类将具有确定其 x、y 位置、方向、速度和知道如何绘制桨的绘制方法的字段。

Game Model 类将具有确定球何时与砖碰撞的方法,确定球何时与砖碰撞的方法,确定球何时与墙壁碰撞的方法,以及调用其他模型类的 draw 方法来绘制球、桨和砖墙。

现在这应该足以让您开始朝着正确的方向前进。

编辑回答问题:

我将如何在所有这些类中实现绘制方法?

这是一个示例 Ball 类。我还没有测试 moveBall 方法,所以它可能需要一些调整

import java.awt.Graphics;
import java.awt.geom.Point2D;

public class Ball {

    private Point2D position;

    /** velocity in pixels per second */
    private double  velocity;

    /**
     * direction in radians
     * <ul>
     * <li>0 - Heading east (+x)</li>
     * <li>PI / 2 - Heading north (-y)</li>
     * <li>PI - Heading west (-x)</li>
     * <li>PI * 3 / 2 - Heading south (+y)</li>
     * </ul>
     * */
    private double  direction;

    public Point2D getPosition() {
        return position;
    }

    public void setPosition(Point2D position) {
        this.position = position;
    }

    public double getVelocity() {
        return velocity;
    }

    public void setVelocity(double velocity) {
        this.velocity = velocity;
    }

    public double getDirection() {
        return direction;
    }

    public void setDirection(double direction) {
        this.direction = direction;
    }

    public void moveBall(long milliseconds) {
        Point2D oldPosition = position;

        // Calculate distance of ball motion
        double distance = velocity / (1000.0D * milliseconds);

        // Calculate new position
        double newX = distance * Math.cos(direction);
        double newY = distance * Math.sin(direction);

        newX = oldPosition.getX() + newX;
        newY = oldPosition.getY() - newY;

        // Update position
        position.setLocation(newX, newY);
    }

    public void draw(Graphics g) {
        int radius = 3;
        int x = (int) Math.round(position.getX());
        int y = (int) Math.round(position.getY());

        // Draw circle of radius and center point x, y
        g.drawOval(x - radius, y - radius, radius + radius, radius + radius);

    }

}

draw 方法将球绘制在实际位置的任何位置。这就是 draw 方法所做的一切。

实际上移动球是游戏模型类的职责。移动球的方法包含在此类中,因为移动球所需的信息存储在 Ball 类中。

我给球的半径为 3,或直径为 6 像素。您可能想要使球更大,并使用 fillOval 方法而不是 drawOval。

我应该每隔 30 毫秒调用一次 repaint()

基本上,是的。

在伪代码中,您创建了一个游戏循环

while (running) {
    update game model();
    draw game();
    wait;
}

首先,您更新游戏模型。我给你上了一堂球课。你会有类似的桨和砖类。它们都有绘制方法。

您的 Game 模型类以正确的顺序调用所有这些绘制方法。在 Breakout 中,您将首先绘制边界,然后是砖块,然后是桨,最后是球。

您的 JPanel(画布)调用 Game Model 类中的一个绘制方法。

我没有向您展示的示例游戏,但是如果您阅读文章Sudoku Solver Swing GUI,您将了解如何组合 Swing GUI,并且您将了解模型类如何实现绘图方法。

我建议您暂时停止使用 Breakout 并阅读Oracle Swing 教程。不要匆忙跳过任何部分来编写程序。通读整个教程,以便在尝试使用之前了解 Swing 的工作原理。

于 2013-05-24T22:13:34.377 回答