繁体   English   中英

为什么我的方法不能正确重绘?

[英]Why doesn't my method repaint correctly?

我最近才开始学习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

您的问题是经典的Swing线程问题,您需要在Swing事件线程上执行长时间运行的任务。 实际上,您似乎正在使用绘画方法执行长时间运行的代码,因此绝对不应执行某些操作,因为这将在每次执行重新绘画时重复执行此任务,从而使绘画速度变慢。

建议:

  • 在诸如SwingWorker提供的后台线程中执行长时间运行的任务,例如读取文件。
  • 仅使用绘画方法绘画,请勿做其他任何事情。
  • 在您的覆盖中调用super的paintComponent方法,以允许JPanel进行其内部管理绘画。
  • 如果您要交换视图 ,请使用CardLayout来方便且安全地进行交换。
  • while (game_running) {循环正在做同样的事情-绑定Swing事件线程,冻结GUI。 为此,请使用Swing计时器。
  • 在绘画方法(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();
   }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM