【问题标题】:Why doesn't my method repaint correctly?为什么我的方法不能正确重绘?
【发布时间】:2014-12-19 21:59:35
【问题描述】:

我最近刚接触 Java,我的目标是用它制作一个简单的图形游戏,所以请随时指出任何风格错误。

从我的主标题屏幕过渡到主屏幕,我的旧标题屏幕没有刷新,用于单击进入主屏幕的按钮被冻结,基本上,图像被冻结并且主屏幕paintComponent没有调用,程序只是进入无限循环并且不会关闭(必须通过任务管理器关闭)。

有趣的是,如果没有 while 循环,它工作得很好,paintComponent 被调用并且一切正常,当重新引入 while 循环时,同样的问题仍然存在。

public class Game {

private static final int HEIGHT = 650;
private static final int WIDTH = 820;
private static final int FRAMES_PER_SEC = 60;
private JFrame frame = new JFrame("Game");
private boolean inIntroScreen = true;
private boolean game_running = false;
private int x  = 1;
private int y  = 1;
private int dx = 1;
private int dy = 1;

/* method to set up GUI for the game. */
public void initGUI () {
    //Build Frame
    frame.setSize(WIDTH, HEIGHT);
    frame.setVisible(true);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setResizable(false);
    //End Build Frame

    /* Intro screen build */
    class drawIntro extends JPanel {

        public void paintComponent(Graphics g) {
            if (inIntroScreen) {
            Graphics2D g2d = (Graphics2D) g;
            //Background
            g2d.setPaint(Color.BLACK);
            g2d.fillRect(0, 0, 820, 650);
            //Title 
            BufferedImage img = null;
            try { img = ImageIO.read(new File("game.png")); }
            catch (IOException e) { System.out.println("Error image"); }
            g2d.drawImage(img, 180, 52, null);

            g2d.setPaint(Color.WHITE);
            g2d.fillOval(550, 60, 40, 40);
            g2d.fillOval(195, 60, 40, 40);
            System.out.println("Intro screen painted");
            }

        } //end paint
    } //end draw inner class

    final drawIntro introScreen = new drawIntro();
    final JPanel introPanel = new JPanel();
    final JButton startButton = new JButton("Start");

    frame.getContentPane().add(introPanel,BorderLayout.SOUTH);
    introPanel.setBackground(Color.BLACK);
    frame.getContentPane().add(introScreen, BorderLayout.CENTER);
    startButton.setPreferredSize(new Dimension(100,50));
    startButton.setBackground(Color.BLACK);
    startButton.setForeground(Color.WHITE);
    introPanel.add(startButton);
    introScreen.repaint();
    //End intro screen build
    startButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            introPanel.removeAll();
            introPanel.revalidate();
            inIntroScreen = false;
            game_running = true;
            System.out.println("button clicked");
            Start();
        }
    });

} //End initGUI

/* Level building class */
class Level extends JPanel {
    @Override 
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        //Background
        g2d.setPaint(Color.BLACK);
        g2d.fillRect(0, 0, 820, 650);
        //Anti-aliasing 
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);

        g2d.setPaint(Color.BLUE);
        g2d.fillOval(x, y, 70, 70);
        x += dx;
        y += dy;


        System.out.println("Main screen painted");
    } //End paint component
}


/* Game loop */
public void Start () {
    Level player = new Level();
    frame.add(player);
    player.repaint();

    int FPS = 1000 / FRAMES_PER_SEC;

        while(game_running) {   /* PROBLEM HERE, if while loop is removed everything works as intended */
        frame.repaint();
        try { Thread.sleep(FPS); } 
        catch (InterruptedException e) {}
        }


}


public static void main(String[] args) {
    Game game = new Game();
    game.initGUI();
    System.out.println("Program terminated");

}

} //end game class

【问题讨论】:

    标签: java swing loops graphics


    【解决方案1】:

    Yours 是一个经典的 Swing 线程问题,您在 Swing 事件线程上执行长时间运行的任务。实际上,您似乎在绘制方法中执行长时间运行的代码,绝对不应该这样做,因为每次执行重绘时都会重复执行此任务,从而减慢您的绘制速度。

    建议:

    • 在 SwingWorker 提供的后台线程中执行长时间运行的任务,例如读取文件。
    • 只用绘画方法绘画,别无其他。
    • 在您的覆盖中调用 super 的 paintComponent 方法,以允许 JPanel 进行其内务绘制。
    • 如果您要交换视图,请使用 CardLayout 以使其变得简单且安全。
    • while (game_running) { 循环也在做同样的事情—— 绑定 Swing 事件线程,冻结你的 GUI。为此,请改用 Swing Timer。
    • 您的绘画方法(paintComponent 方法)中有游戏逻辑,您可以在其中设置 x 和 y 变量。不要这样做,而是在您的 Swing Timer 代码中更改它们。您永远无法完全控制是否或是否调用 paintComponent 方法,因此您不希望在此方法中包含程序逻辑和更改字段的代码。

    例如:

    // start method name should start with a lower-case letter
    public void start() {
      final Level player = new Level();
      frame.add(player);
      player.repaint();
    
      int fps = 1000 / FRAMES_PER_SEC;
    
      // use a field called timer
      timer = new Timer(fps, new ActionListener() {
    
         @Override
         public void actionPerformed(ActionEvent e) {
            // get this out of the paintComponent method
            x += dx;
            y += dy;
            player.repaint();
         }
      });
      timer.start();
    }
    

    例如:

    import java.awt.BorderLayout;
    import java.awt.CardLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.RenderingHints;
    import java.awt.event.*;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.net.URL;
    
    import javax.imageio.ImageIO;
    import javax.swing.*;
    
    @SuppressWarnings("serial")
    public class Game2 extends JPanel {
       public static final String INTRO = "intro";
       public static final String GAME = "game";
       public static final int FPS = 15;
       private CardLayout cardLayout = new CardLayout();
    
       public Game2() throws IOException {
          URL imgUrl = new URL(IntroScreen.IMAGE_PATH);
          BufferedImage img = ImageIO.read(imgUrl);
          IntroScreen introScreen = new IntroScreen(img);
          introScreen.setLayout(new BorderLayout());
    
          JButton startButton = new JButton(new StartAction("Start"));
          JPanel bottomPanel = new JPanel();
          bottomPanel.setOpaque(false);
          bottomPanel.add(startButton);
          introScreen.add(bottomPanel, BorderLayout.PAGE_END);
    
          setLayout(cardLayout);
          add(introScreen, INTRO);
       }
    
       private class StartAction extends AbstractAction {
          public StartAction(String name) {
             super(name);
             int mnemonic = (int) name.charAt(0);
             putValue(MNEMONIC_KEY, mnemonic);
          }
    
          @Override
          public void actionPerformed(ActionEvent e) {
             GamePanel gamePanel = new GamePanel(FPS);
             Game2.this.add(gamePanel, GAME);
             cardLayout.show(Game2.this, GAME);
             gamePanel.start();
          }
       }
    
       private static void createAndShowGui() {
          Game2 game2 = null;
          try {
             game2 = new Game2();
          } catch (IOException e) {
             e.printStackTrace();
             System.exit(-1);
          }
    
          JFrame frame = new JFrame("Game");
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          frame.getContentPane().add(game2);
          frame.pack();
          frame.setLocationRelativeTo(null);
          frame.setVisible(true);
       }
    
       public static void main(String[] args) {
          SwingUtilities.invokeLater(new Runnable() {
             public void run() {
                createAndShowGui();
             }
          });
       }
    }
    
    @SuppressWarnings("serial")
    class IntroScreen extends JPanel {
       public static final String IMAGE_PATH = "https://duke.kenai.com/"
             + "glassfish/GlassFishMedium.jpg";
       private BufferedImage img;
    
       public IntroScreen(BufferedImage img) {
          this.img = img;
       }
    
       @Override
       protected void paintComponent(Graphics g) {
          super.paintComponent(g);
          if (img != null) {
             g.drawImage(img, 0, 0, this);
          }
       }
    
       @Override
       public Dimension getPreferredSize() {
          if (img != null) {
             int width = img.getWidth();
             int height = img.getHeight();
             return new Dimension(width, height);
          }
          return super.getPreferredSize();
       }
    }
    
    @SuppressWarnings("serial")
    class GamePanel extends JPanel {
       protected static final int DX = 2;
       protected static final int DY = DX;
       private int x;
       private int y;
       private Timer timer;
       private int fps = 0;
    
       public GamePanel(int fps) {
          this.fps = fps;
       }
    
       @Override 
       public void paintComponent(Graphics g) {
           super.paintComponent(g);
           Graphics2D g2d = (Graphics2D) g;
           //Background
           g2d.setPaint(Color.BLACK);
           g2d.fillRect(0, 0, 820, 650);
           //Anti-aliasing 
           g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
           g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    
           g2d.setPaint(Color.BLUE);
           g2d.fillOval(x, y, 70, 70);
       }
    
       public void start() {
          // use a field called timer
          timer = new Timer(fps, new ActionListener() {
    
             @Override
             public void actionPerformed(ActionEvent e) {
                // get this out of the paintComponent method
                x += DX;
                y += DY;
                repaint();
             }
          });
          timer.start();
       }
    }
    

    【讨论】:

      猜你喜欢
      • 2011-10-07
      • 2016-07-12
      • 1970-01-01
      • 1970-01-01
      • 2019-02-03
      • 2022-01-03
      • 1970-01-01
      • 2010-10-13
      • 1970-01-01
      相关资源
      最近更新 更多