2014-12-19 39 views
1

我剛剛拿起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 

回答

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(); 
    } 
} 
相關問題