简体   繁体   English

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

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

I've just recently picked up Java and I'm aiming to make a simple graphical game with it so feel free to point out any style mistakes. 我最近才开始学习Java,我的目标是用它制作一个简单的图形游戏,以便随时指出任何样式错误。

In transition from my main title screen to the main screen, my old title screen is not refreshed, the button used to click to get to the main screen is frozen, basically, the image is frozen and the main screen paintComponent is not called and the program just goes into an infinite loop and will not close (have to shut down via task manager). 从我的主标题屏幕过渡到主屏幕时,我的旧标题屏幕没有刷新,用于单击以转到主屏幕的按钮被冻结,基本上,图像被冻结,主屏幕上的paintComponent没有被调用,并且程序只是进入无限循环而不会关闭(必须通过任务管理器关闭)。

Interesting thing to note is that, without the while loop it works just fine, the paintComponent is called and everything works as it should, when reintroducing the while loop, same problem persists. 需要注意的有趣一点是,没有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

Yours is a classic Swing threading issue where you perform a long-running task on the Swing event thread. 您的问题是经典的Swing线程问题,您需要在Swing事件线程上执行长时间运行的任务。 You appear in fact to be doing long-running code in the painting method, something that should absolutely never be done since this will repeatedly perform this task every time a repaint is performed, slowing your painting to a crawl. 实际上,您似乎正在使用绘画方法执行长时间运行的代码,因此绝对不应执行某些操作,因为这将在每次执行重新绘画时重复执行此任务,从而使绘画速度变慢。

Suggestions: 建议:

  • Do long-running tasks, such as reading files, in a background thread such as provided by a SwingWorker. 在诸如SwingWorker提供的后台线程中执行长时间运行的任务,例如读取文件。
  • Do only painting in paint methods, and nothing else. 仅使用绘画方法绘画,请勿做其他任何事情。
  • Call the super's paintComponent method in your override to allow the JPanel to do its house-keeping painting. 在您的覆盖中调用super的paintComponent方法,以允许JPanel进行其内部管理绘画。
  • If you are going to be swapping views , use a CardLayout to make it easy and safe to do so. 如果您要交换视图 ,请使用CardLayout来方便且安全地进行交换。
  • The while (game_running) { loop is doing the same thing -- tying up the Swing event thread, freezing your GUI. while (game_running) {循环正在做同样的事情-绑定Swing事件线程,冻结GUI。 Use a Swing Timer instead for this. 为此,请使用Swing计时器。
  • You've got game logic in your painting method (the paintComponent method), where you're setting the x and y variables. 在绘画方法(paintComponent方法)中已经有了游戏逻辑,您可以在其中设置x和y变量。 Don't do this, but instead change them in your Swing Timer's code. 不要这样做,而是在Swing Timer的代码中更改它们。 You never have complete control over whether or if the paintComponent method is called, and so you want to have no program logic, no code that changes fields, within this method. 您永远无法完全控制是否调用paintComponent方法,因此,您希望在此方法内没有程序逻辑,也没有更改字段的代码。

For example: 例如:

// 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();
}

For example: 例如:

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