2014-12-19 36 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(); 
    } 
} 
相关问题