java - 如何绘制两个重叠的JPanel,使它们绘制的图形都显示在屏幕上?

标签 java swing jpanel graphics2d game-development

我有两个 JPanel。一个面板在 0,0 处绘制了一个 100x100 的矩形。另一个在 100、100 处绘制了一个 100x100 的矩形。我的问题是,当两个 JPanel 都绘制在 JFrame 的内容 Pane 上时,一个 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。上面绘制了砖 block 的 JPanel。还有一个上面画了一个球的 JPanel。我的理由是,如果桨没有被移动、背景和没有被击中的砖 block ,我就不必重新绘制它。如果一 block 砖被击中,我将更新一个砖的 arrayList 并在相应的 JPanel 上调用重绘。

最佳答案

I am a novice programmer and I have no knowledge of gaming theory.

好的,我们可以解决这个问题。

So I decided the smartest way to avoid a lot of rendering and buffering is to have four JPanels.

您只是不必要地使程序复杂化。

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

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

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

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

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

游戏模型类将具有确定球何时与砖碰撞、确定球何时与砖碰撞、确定球何时与墙碰撞的方法,以及调用其他模型类绘制方法的绘制方法画一个球、一个桨和一堵砖墙。

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

编辑回答问题:

How would I implement a draw method in all these classes?

这是一个示例 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。

should I just call repaint() at a 30ms interval

基本上,是的。

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

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

首先,您更新游戏模型。我给你上了球课。对于桨和积木,您会有类似的类(class)。它们都有绘制方法。

您的游戏模型类以正确的顺序调用所有这些绘制方法。在 Breakout 中,您会先画出边界,然后是砖 block ,然后是 Racket ,最后是球。

您的 JPanel( Canvas )调用游戏模型类中的一个绘制方法。

我没有示例游戏可以向您展示,但如果您阅读文章 Sudoku Solver Swing GUI ,您将看到如何组合一个 Swing GUI,您将看到模型类如何实现绘制方法。

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

关于java - 如何绘制两个重叠的JPanel,使它们绘制的图形都显示在屏幕上?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16739225/

相关文章:

java - 如何在Java、Apache POI中获取excel单元格字段的字体样式?

java - 我的主类应该在项目中的什么位置创建?

java - 带标题的 JPanel

java - 一个 JFrame 和 50 个 JPanel 还是只有 50 个 JFrame 或其他选择?

java - sessionTransacted 和 JmsTransactionManager 的区别

java - Spring OAuth2 "Full authentication is required to access this resource"

java - 如何配置服务器上的聚光灯以提供与网络上的聚光灯演示相同的输出?

java - 如何在点击图像时生成事件?

Java改变JFrame内容的问题

java - 防止 Java 在更新时重新绘制 JPanel 的内容