从多个地方在 JPanel 上绘图

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

我目前正在为学校开发一款 Java 2D 游戏。我们必须使用抽象工厂设计模式。对于 2D 实现,我使用工厂如下:

public class Java2DFact extends AbstractFactory {
    public Display display;
    private Graphics g;
    public Java2DFact() {
        display = new Display(2000, 1200);
    }


    @Override
    public PlayerShip getPlayership()
    {
        return new Java2DPlayership(display.panel);
    }

在我的显示类中,我创建了 JFrame 和 Jpanel

public class Display {
    public JFrame frame;
    public JPanel panel;
    public int width, height;

    public Display(int width, int height) {
        this.width = width;
        this.height = height;

        frame = new JFrame();
        frame.setTitle("SpaceInvaders");
        frame.setSize(1200,800);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        
        panel = new JPanel(){
           @Override
            protected void paintComponent(Graphics g){
               super.paintComponent(g);
            }
        };
        panel.setFocusable(true);
        frame.add(panel);
    }
}

现在,从我的主游戏循环中,我调用 Java2DPLayership 类中的可视化方法来可视化我的 Playership

public class Java2DPlayership extends PlayerShip  {
    private JPanel panel;
    private Graphics2D g2d;
    private Image image;
    private BufferStrategy bs;


    public Java2DPlayership(JPanel panel) {
        super();
        this.panel = panel;
    }

    public void visualize()  {
        try {
            image = ImageIO.read(new File("src/Bee.gif"));
            Graphics2D g = (Graphics2D) bs.getDrawGraphics();
            //g.setColor(new Color(0, 0, 0));
            //g.fillRect(10, 10, 12, 8);
            g.drawImage(image, (int) super.getMovementComponent().x, (int) super.getMovementComponent().y, null);
            Toolkit.getDefaultToolkit().sync();
            g.dispose();
            panel.repaint();
        } catch(Exception e){
            System.out.println(e.toString());
        }
    }
}

我的目标是将 JPanel 传递给每个实体,并让它在显示面板之前将其内容绘制到面板上。但是我似乎不知道如何做到这一点。当通过更改面板的图形来使用这种方法时,我会出现很多闪烁。

java swing jpanel 2d
1个回答
3
投票

这是我前段时间写的一个功能齐全但很简单的示例。它只是有一堆

balls
从面板侧面弹起。 请注意,
render
Ball class
方法接受来自
paintComponent
的图形上下文。 如果我有更多的类需要渲染,我可以创建一个
Renderable
接口并让每个类实现它。 然后我可以有一个
Renderable
对象列表,然后遍历它们并调用该方法。但正如我也说过的,这需要尽快完成,以避免占用 EDT。

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Bounce extends JPanel {
   private static final int    COLOR_BOUND  = 256;
   private final static double INC          = 1;
   private final static int    DIAMETER     = 40;
   private final static int    NBALLS       = 20;
   private final static int    DELAY        = 5;
   private final static int    PANEL_WIDTH  = 800;
   private final static int    PANEL_HEIGHT = 600;
   private final static int    LEFT_EDGE    = 0;
   private final static int    TOP_EDGE     = 0;
   private JFrame              frame;
   private double              rightEdge;
   private double              bottomEdge;
   private List<Ball>          balls        = new ArrayList<>();
   private Random              rand         = new Random();
   private List<Long>          times        = new ArrayList<>();
   private int width;
   private int height;
   
   public Bounce(int width, int height) {
      for (int j = 0; j < NBALLS; j++) {
           int r = rand.nextInt(COLOR_BOUND);
           int g = rand.nextInt(COLOR_BOUND);
           int b = rand.nextInt(COLOR_BOUND);
           Ball bb = new Ball(new Color(r, g, b), DIAMETER);
           balls.add(bb);
           
      }
      this.width = width;
      this.height = height;
      frame = new JFrame("Bounce");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.add(this);
      addComponentListener(new MyComponentListener());
      frame.pack();
      frame.setLocationRelativeTo(null);
      rightEdge = width - DIAMETER;
      bottomEdge = height - DIAMETER;
    
      frame.setVisible(true);
   }

   public Dimension getPreferredSize() {
       return new Dimension(width, height);
   }
   
   public static void main(String[] args) {
      new Bounce(PANEL_WIDTH, PANEL_HEIGHT).start();
   }

   public void start() {
      /**
       * Note: Using sleep gives a better response time than
       * either the Swing timer or the utility timer. For a DELAY
       * of 5 msecs between updates, the sleep "wakes up" every 5
       * to 6 msecs while the other two options are about every
       * 15 to 16 msecs. Not certain why this is happening though
       * since the other timers are run on threads.
       * 
       */
     Timer timer = new Timer(0,(ae)-> {repaint();
         for (Ball b : balls) {
            b.updateDirection(); 
         }} );
      timer.setDelay(5); // 5 ms.
      timer.start();
   }
   
  
   public void paintComponent(Graphics g) {
      super.paintComponent(g);
      Graphics2D g2d = (Graphics2D) g;
      g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
      
      for (Ball ball : balls) {
         ball.render(g2d);
      }
   }

   class MyComponentListener extends ComponentAdapter {
      public void componentResized(ComponentEvent ce) {
         Component comp = ce.getComponent();
         rightEdge = comp.getWidth() - DIAMETER;
         bottomEdge = comp.getHeight() - DIAMETER;
         for (Ball b : balls) {
            b.init();
         }
      }
   }
   class Ball {
      private Color  color;
      public double  x;
      private double y;
      private double yy;
      private int    ydir = 1;
      private int    xdir = 1;
      private double slope;
      private int    diameter;

      public Ball(Color color, int diameter) {
         this.color = color;
         this.diameter = diameter;
         init();
      }

      public void init() {
         // Local constants not uses outside of method
         // Provides default slope and direction for ball
         slope = Math.random() * .25 + .50;
         x = (int) (rightEdge * Math.random());
         yy = (int) (bottomEdge * Math.random()) + diameter;
         xdir = Math.random() > .5 ? -1
                                   : 1;
         ydir = Math.random() > .5 ? -1
                                   : 1;
         y = yy;
      }

      public void render(Graphics2D g2d) {
         g2d.setColor(color);
         g2d.fillOval((int) x, (int) y, diameter, diameter);
      }

      public void updateDirection() {
         x += (xdir * INC);
         yy += (ydir * INC);
         y = yy * slope;
         if (x < LEFT_EDGE || x > rightEdge) {
            xdir = -xdir;
         }
         if (y < TOP_EDGE || y > bottomEdge) {
            ydir = -ydir;
         }
      }
   }
}
© www.soinside.com 2019 - 2024. All rights reserved.