为什么我的方法没有正确重绘?

时间:2014-12-19 21:59:36

标签: java swing loops graphics

我刚刚选择了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

1 个答案:

答案 0 :(得分:2)

你的是一个经典的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();
   }
}