简体   繁体   English

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

[英]How to use the repaint() method with a game loop

I'm trying to learn how to create a game loop with java and draw a new screen a certain amount of times per second. 我正在尝试学习如何使用Java创建游戏循环并每秒绘制一定数量的新屏幕。 The game loop is working fine but when I try to call the paint method with repaint() the paint method is not called. 游戏循环运行正常,但是当我尝试使用repaint()调用paint方法时,未调用paint方法。 Here is my code: 这是我的代码:

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");
}

}

Hello is printed out many times but Goodbye is never called, this leads me to believe that the paint method is never called. 您好,打印很多次,但从未调用过再见,这使我相信从未调用过paint方法。 Why won't the repaint method update my paint method? 为什么重绘方法不会更新我的绘画方法?

You never add your MyCanvas2 object to anything, so nothing appears to be rendered, and thus painted. 您永远不会将MyCanvas2对象添加到任何对象中,因此似乎没有任何东西呈现和绘制。 My main recommendation is thus that you add the MyCanvas2 JPanel to your GUI if you want it rendered. 因此,我的主要建议是,如果要渲染MyCanvas2 JPanel,则将其添加到GUI中。 Also: 也:

  • You never call createFrame() , and so the GUI (JFrame) is never created -- so make sure that you call this. 您永远不会调用createFrame() ,因此永远不会创建GUI(JFrame)-因此请确保调用此方法。
  • Your gameStart() method, an instance method of MainFrame creates a new MainFrame instance, a dangerous and unnecessary recursion -- don't do this. 您的gameStart()方法(MainFrame的实例方法)会创建一个新的 MainFrame实例,这是危险且不必要的递归-请勿执行此操作。

Other side recommendations and notes: 其他建议和注意事项:

  • You will want to override paintComponent , not paint since this will automatically give you double buffering and thus a smoother animation/graphics. 您将要覆盖paintComponent 而不是 paint,因为这将自动为您提供双重缓冲,从而使动画/图形更加流畅。
  • Don't forget to call the super's painting method within your override so as not to break the painting chain. 不要忘记在覆盖范围内调用super的绘画方法,以免破坏绘画链。 So if you override the paintComponent method, then call the super paintComponent method. 因此,如果您覆盖paintComponent方法,则调用super paintComponent方法。
  • Calling repaint() does not guarantee that a painting will be done as it is just a suggestion. 调用repaint()不能保证绘画会完成,因为它只是一个建议。 If repaint requests get stacked up, for instance due to heavy processing or too short of a wait time, then some repaint requests will be ignored. 如果重涂请求堆积起来,例如由于处理繁重或等待时间太短,那么一些重涂请求将被忽略。
  • In general, you should avoid extending Thread and instead implementing Runnable, or here, using a Swing Timer. 通常,应避免扩展Thread,而应使用Swing Timer来实现Runnable或此处。
  • You will want to learn and follow Java naming rules so as not to confuse others who are trying to read and understand your program and help you. 您将要学习并遵循Java命名规则,以免使试图阅读和理解您的程序并为您提供帮助的其他人感到困惑。

For example here that uses a Swing Timer and does a simple animation: 例如,这里使用Swing计时器并执行简单的动画:

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

I had another issue , with flickering components. 我还有另一个问题 ,组件闪烁

The solution I found was to stop asking for repaints outside the event disspatch thread. 我发现的解决方案是停止在事件分发线程之外请求重绘。
(source : http://www.java-gaming.org/topics/double-buffering-with-a-jpanel/33983/view.html ) (来源: http : //www.java-gaming.org/topics/double-buffering-with-a-jpanel/33983/view.html

Here's the code I used : 这是我使用的代码:

SwingUtilities.invokeLater(() -> {  // Runs update on the EDT (JDK8 isn't necessary)
    vue.refreshViewPane(); //All my components&redrawing soup is here
});

I commented out my calls to repaint() & revalidate in my "refresh" method, they weren't even necessary. 我用“刷新”方法注释掉了对repaint()和revalidate的调用,它们甚至不是必需的。 I only added a boolean to be sure I skip the whole soup if a "refresh" was already pending, but performances seemed smooth anyway, at the 30 FPS I'm currently using. 我只是添加了一个布尔值,以确保如果“刷新”已经在进行中,我会跳过整个汤,但是无论如何,在我当前使用的30 FPS上,性能似乎还是很流畅的。
The "soup" content is basically emptying 3 overlapping JPanels ( Terrains, units, selection ) in a JLayeredPane and filling them with other images representing my game's elements. “汤”的内容基本上是在JLayeredPane中清空3个重叠的JPanels( 地形,单位,选择 ),并用代表我的游戏元素的其他图像填充它们。

I'm not using Canvas for my graphics, only ImageIcon (which might be terrible performances-wise... I have no idea.) Anyway, My game is turn-based , not realtime, so as long as cursor and units move smoothly, I don't worry much. 我没有在我的图形上使用Canvas,而只是在ImageIcon上使用(这可能会带来糟糕的性能……我不知道。)无论如何,只要光标和单位能够平稳移动,我的游戏都是基于回合制的 ,而不是实时的,我不用担心。

I answer here mainly because that's where Google brought me when I asked how to repaint efficently in a Java gameloop, so I think my two cents might help someone. 我在这里回答的主要原因是,当我问到如何有效地在Java游戏循环中重新粉刷时,这就是Google带来的。


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

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