如何在游戏循环中使用 repaint() 方法

Pri*_*ank 0 java swing repaint game-loop graphics2d

我正在尝试学习如何使用 java 创建一个游戏循环并每秒绘制一个新屏幕一定次数。游戏循环工作正常,但是当我尝试使用 repaint() 调用paint方法时,未调用paint方法。这是我的代码:

import javax.swing.JButton;
import javax.swing.JComponent;
import java.awt.Graphics;
import javax.swing.JFrame;
import java.awt.image.*;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;

public class mainFrame extends Thread{
static boolean gameIsRunning = false;
MyCanvas2 myCanvas2 = new MyCanvas2();
static final int TARGET_FPS = 1;
static int x = 10;
static int y = 10;
static long startTime = 0;
static long elapsedTime = 0;
static long waitTime = 0;
public void createFrame(){
  JFrame window = new JFrame("Out from Eden");
  window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  window.setBounds(30, 30, 700, 500);
  window.setVisible(true);
  gameIsRunning = true;
  gameStart();

}

public void setGame(boolean game){
    gameIsRunning = game;
}

public void gameStart(){
    (new Thread(new mainFrame())).start();
}


public void run(){
        while(gameIsRunning == true){
           startTime = System.nanoTime();
           myCanvas2.updateGame();
           myCanvas2.renderGame();
           elapsedTime = System.nanoTime() - startTime;
           waitTime = (TARGET_FPS*10) - (elapsedTime / 1000000);

           if (waitTime < 0) {
               waitTime = 5;
            }


           try {
                    Thread.sleep(waitTime);
               } catch (Exception e) {
                    e.printStackTrace();
               }

        }
    }
}






class MyCanvas2 extends JPanel{
private static int x = 100;
private static int y = 100;
static int i =0;
public void updateGame(){

}

public void renderGame(){
    repaint();
    System.out.println("Hello");
}


public void paint(Graphics g){

    Graphics2D g2 = (Graphics2D) g;
    g.setColor(Color.blue);
    g2.drawString("Test", x,y);
    System.out.println("Goodbye");
}

}
Run Code Online (Sandbox Code Playgroud)

Hello 被打印了很多次,但是 Goodbye 从来没有被调用过,这让我相信 Paint 方法从来没有被调用过。为什么重绘方法不会更新我的绘画方法?

Hov*_*els 5

你永远不会将你的 MyCanvas2 对象添加到任何东西,所以似乎没有任何东西被渲染,因此没有被绘制。因此,我的主要建议是将 MyCanvas2 JPanel 添加到您的 GUI 中,如果您想要它呈现的话。还:

  • 您永远不会调用createFrame(),因此永远不会创建 GUI(JFrame)——因此请确保您调用它。
  • 您的gameStart()方法, MainFrame 的一个实例方法会创建一个新的MainFrame 实例,这是一个危险且不必要的递归——不要这样做。

其他方面的建议和注意事项:

  • 您将需要覆盖paintComponent而不是绘制,因为这会自动为您提供双缓冲,从而获得更流畅的动画/图形。
  • 不要忘记在覆盖中调用 super 的绘制方法,以免破坏绘制链。所以如果你重写paintComponent方法,那么调用superpaintComponent方法。
  • 打电话repaint()并不能保证一定会完成一幅画,因为这只是一个建议。如果重绘请求堆积起来,例如由于处理繁重或等待时间太短,那么一些重绘请求将被忽略。
  • 通常,您应该避免扩展 Thread,而是实现 Runnable,或者在这里使用 Swing Timer。
  • 您将需要学习并遵循 Java 命名规则,以免混淆试图阅读和理解您的程序并帮助您的其他人。

例如这里使用 Swing Timer 并执行简单的动画:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

public class MainGui {

   private static final int TIMER_DELAY = 100;

   private static void createAndShowGui() {
      // create our JPanel
      MainGuiPanel mainPanel = new MainGuiPanel();
      // and create our ActionListener for the Swing Timer
      MyTimerListener myTimerListener = new MyTimerListener(mainPanel);

      // create a JFrame
      JFrame frame = new JFrame("Main Gui");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

      // add our JPanel to it
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);  // display the GUI

      // create and start the Swing Timer
      new Timer(TIMER_DELAY, myTimerListener).start();
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

@SuppressWarnings("serial")
class MainGuiPanel extends JPanel {
   private static final int PREF_W = 700;
   private static final int PREF_H = 500;
   private int textX = 0;
   private int textY = 10;

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      g.setColor(Color.blue);
      g.drawString("Test", textX, textY);
      System.out.println("Goodbye");
   }

   @Override
   // make our GUI larger
   public Dimension getPreferredSize() {
      Dimension superSz = super.getPreferredSize();
      if (isPreferredSizeSet()) {
         return superSz;
      }
      int prefW = Math.max(superSz.width, PREF_W);
      int prefH = Math.max(superSz.height, PREF_H);
      return new Dimension(prefW, prefH);
   }

   public void moveText() {
      textX++;
      textY++;
      repaint();
   }
}

// ActionListener for Swing Timer that drives the game loop
class MyTimerListener implements ActionListener {
   private MainGuiPanel mainGuiPanel;

   public MyTimerListener(pkg.MainGuiPanel mainGuiPanel) {
      this.mainGuiPanel = mainGuiPanel;
   }

   @Override
   // this method gets called each time the timer "ticks"
   public void actionPerformed(ActionEvent e) {
      // make sure our GUI is not null and is displayed
      if (mainGuiPanel != null && mainGuiPanel.isDisplayable()) {
         // call method to animate. 
         mainGuiPanel.moveText(); // this method calls repaint
      } else {
         ((Timer) e.getSource()).stop();
      }      
   }
}
Run Code Online (Sandbox Code Playgroud)