0

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. The game loop is working fine but when I try to call the paint method with repaint() the paint method is not called. 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. Why won't the repaint method update my paint method?

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
Priyank
  • 49
  • 1
  • 2
  • 6

2 Answers2

5

You never add your MyCanvas2 object to anything, so nothing appears to be rendered, and thus painted. My main recommendation is thus that you add the MyCanvas2 JPanel to your GUI if you want it rendered. Also:

  • You never call createFrame(), and so the GUI (JFrame) is never created -- so make sure that you call this.
  • Your gameStart() method, an instance method of MainFrame creates a new MainFrame instance, a dangerous and unnecessary recursion -- don't do this.

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.
  • Don't forget to call the super's painting method within your override so as not to break the painting chain. So if you override the paintComponent method, then call the super paintComponent method.
  • Calling repaint() does not guarantee that a painting will be done as it is just a suggestion. 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.
  • 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.

For example here that uses a Swing Timer and does a simple animation:

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();
      }      
   }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • I will add the MyCanvas2 object to the window but the method isn't even being called as there is no output, I was wondering why the paint method isn't being called. – Priyank Jun 07 '15 at 23:58
  • Ah so, is the paint method not being called because I have too many calls to repaint? – Priyank Jun 08 '15 at 00:04
  • I'm sorry, I don't understand, where is the error, is it in the paint method? Should I call super? I'm just trying to get 'Goodbye' to display right now. – Priyank Jun 08 '15 at 00:08
  • Also, createFrame() is called from a different class. – Priyank Jun 08 '15 at 00:37
0

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)

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. 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.
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.

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.

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.


Balmipour
  • 2,985
  • 1
  • 24
  • 28