0

It seems like this applet will only draw and update DRAWS when the window is resized or minimized. So the applet will not repaint all the time, but only when manipulating the window.

Am I doing something wrong here?

I am following the gameloop presented here: http://www3.ntu.edu.sg/home/ehchua/programming/java/J8d_Game_Framework.html

The code is here:

package newapplet;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class GameApplet extends JApplet {     // main class for the game as a Swing application

// Define constants for the game
static final int CANVAS_WIDTH = 493;    // width and height of the game screen
static final int CANVAS_HEIGHT = 411;
static final int UPDATE_RATE = 4;    // number of game update per second
static final long UPDATE_PERIOD = 1000000000L / UPDATE_RATE;  // nanoseconds

static int DRAWS = 0;
// ......

// Enumeration for the states of the game.
public enum gameState {
  INITIALIZED, CONNECTING, PLAYING, DISCONNECTED
}

private gameState state;

// Define instance variables for the game objects
// ......
// ......

// Handle for the custom drawing panel
private GameCanvas canvas;

// Constructor to initialize the UI components and game objects
public GameApplet() {
  // Initialize the game objects
  gameInit();

  // UI components
  canvas = new GameCanvas();
  canvas.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
  this.setContentPane(canvas);

  // Other UI components such as button, score board, if any.
  // ......

  this.setVisible(true);
}

// All the game related codes here

// Initialize all the game objects, run only once in the constructor of the main class.
public void gameInit() {
  // ...... 
  state = gameState.INITIALIZED;
}

// Shutdown the game, clean up code that runs only once.
public void gameShutdown() {
  // ...... 
   state = gameState.DISCONNECTED;
}

// To start and re-start the game.
public void gameStart() { 
  // Create a new thread
  Thread gameThread =  new Thread() {
     // Override run() to provide the running behavior of this thread.
     @Override
     public void run() {
        gameLoop();
     }
  };
  // Start the thread. start() calls run(), which in turn calls gameLoop().
  gameThread.start();
}

// Run the game loop here.
private void gameLoop() {
  // Regenerate the game objects for a new game
  // ......
  //state = State.PLAYING;

  // Game loop
  long beginTime, timeTaken, timeLeft;
  while (true) {
     beginTime = System.nanoTime();
     if (state == gameState.DISCONNECTED) break;  // break the loop to finish the current play
     if (state == gameState.PLAYING) {
        // Update the state and position of all the game objects,
        // detect collisions and provide responses.
        gameUpdate();
     }
     // Refresh the display
     repaint();
     // Delay timer to provide the necessary delay to meet the target rate
     timeTaken = System.nanoTime() - beginTime;
     timeLeft = (UPDATE_PERIOD - timeTaken) / 1000000L;  // in milliseconds
     if (timeLeft < 10) timeLeft = 10;   // set a minimum
     try {
        // Provides the necessary delay and also yields control so that other thread can do work.
        Thread.sleep(timeLeft);
     } catch (InterruptedException ex) { }
  }
}

// Update the state and position of all the game objects,
// detect collisions and provide responses.
public void gameUpdate() { 

}

// Refresh the display. Called back via rapaint(), which invoke the paintComponent().
private void gameDraw(Graphics2D g2d) {
  switch (state) {
     case INITIALIZED:
    g2d.setColor (Color.red); 
    g2d.drawString ("init",20,20);
    break;
     case PLAYING:
    g2d.setColor (Color.red); 
    g2d.drawString ("play",20,20);
    break;
  case CONNECTING:
    g2d.setColor (Color.red); 
    g2d.drawString ("connecting",20,20);
    break;
  case DISCONNECTED:
    g2d.setColor (Color.red); 
    g2d.drawString ("disconnect",20,20);
    break;
  }

  g2d.setColor (Color.GREEN); 
  g2d.drawString ("Re-paint: " + DRAWS,30,30);
  this.DRAWS++;
  // ...... 
}

// Process a key-pressed event. Update the current state.
public void gameKeyPressed(int keyCode) {
  switch (keyCode) {
     case KeyEvent.VK_UP:
        // ......
        break;
     case KeyEvent.VK_DOWN:
        // ......
        break;
     case KeyEvent.VK_LEFT:
        // ......
        break;
     case KeyEvent.VK_RIGHT:
        // ......
        break;
  }
}

// Process a key-released event.
public void gameKeyReleased(int keyCode) {  }

// Process a key-typed event.
public void gameKeyTyped(char keyChar) {  }

// Other methods
// ......

// Custom drawing panel, written as an inner class.
class GameCanvas extends JPanel implements KeyListener {
  // Constructor
  public GameCanvas() {
     setFocusable(true);  // so that can receive key-events
     requestFocus();
     addKeyListener(this);
  }

  // Override paintComponent to do custom drawing.
  // Called back by repaint().
  @Override
  public void paintComponent(Graphics g) {
     Graphics2D g2d = (Graphics2D)g;
     super.paintComponent(g2d);   // paint background
     setBackground(Color.BLACK);  // may use an image for background

     // Draw the game objects
     gameDraw(g2d);
  }

  // KeyEvent handlers
  @Override
  public void keyPressed(KeyEvent e) {
     gameKeyPressed(e.getKeyCode());
  }

  @Override
  public void keyReleased(KeyEvent e) {
     gameKeyReleased(e.getKeyCode());
  }

  @Override
  public void keyTyped(KeyEvent e) {
     gameKeyTyped(e.getKeyChar());
  }
}

// main
public static void main(String[] args) {
  // Use the event dispatch thread to build the UI for thread-safety.
  SwingUtilities.invokeLater(new Runnable() {
     @Override
     public void run() {
        new GameApplet();
     }
  });
}
}
Jonas
  • 121,568
  • 97
  • 310
  • 388
SCH
  • 13
  • 4
  • 2
    That 200+ lines of code has `10 errors` at compilation. For better help sooner, post an [SSCCE](http://sscce.org/). – Andrew Thompson Dec 29 '11 at 02:17
  • One definite problem I see is that you're calling `repaint()` outside of the event thread. – BillRobertson42 Dec 29 '11 at 02:28
  • 1
    @AndrewThompson: Done, replaced the version with another one and sorry. – SCH Dec 29 '11 at 02:30
  • @Bill: How can I avoid that, and what should be in charge of repainting? I am not that much into Applets, and just played around with some code that I found, that looked comprehensible. – SCH Dec 29 '11 at 02:33
  • I did a bit of googling for 'java animation' and came up with more than a few tutorials. You might want to start with something really simple, e.g. a clock and ask very targeted questions when you run into trouble. Then move up to the bigger things. – BillRobertson42 Dec 29 '11 at 02:40

2 Answers2

1

Glancing at that code quickly I can see that you are calling repaint() outside of the Event Dispatch Thread, which can cause issues like the one you are seeing. javax.swing.SwingUtilties.invokeAndWait(Runnable r) will allow you to put that repaint() call on the EDT.

http://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html

Scott Faria
  • 270
  • 2
  • 10
1

GameApplet

See this code for a few corrections.

// <applet code='GameApplet' width=400 height=50></applet>

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class GameApplet extends JApplet {     // main class for the game as a Swing application

// Define constants for the game
static final int CANVAS_WIDTH = 493;    // width and height of the game screen
static final int CANVAS_HEIGHT = 411;
static final int UPDATE_RATE = 4;    // number of game update per second
static final long UPDATE_PERIOD = 1000000000L / UPDATE_RATE;  // nanoseconds
Timer timer;

static int DRAWS = 0;
// ......

// Enumeration for the states of the game.
public enum gameState {
  INITIALIZED, CONNECTING, PLAYING, DISCONNECTED
}

private gameState state;

// Define instance variables for the game objects
// ......
// ......

// Handle for the custom drawing panel
private GameCanvas canvas;

// Constructor to initialize the UI components and game objects
public GameApplet() {
  // Initialize the game objects
  gameInit();

  // UI components
  canvas = new GameCanvas();
  // set the size of the applet in HTML, not the content pane!
  canvas.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
  this.setContentPane(canvas);

  // Other UI components such as button, score board, if any.
  // ......

  //this.setVisible(true);
}

// All the game related codes here

// Initialize all the game objects, run only once in the constructor of the main class.
public void gameInit() {
  // ......
  state = gameState.INITIALIZED;
  gameStart();
}

// Shutdown the game, clean up code that runs only once.
public void gameShutdown() {
  // ......
   state = gameState.DISCONNECTED;
}

@Override
public void destroy() {
    timer.stop();
}

// To start and re-start the game.
public void gameStart() {
  // Create a new thread
  //Thread gameThread =  new Thread() {
     // Override run() to provide the running behavior of this thread.
  //   @Override
  //   public void run() {
        gameLoop();
  //   }
  //};
  // Start the thread. start() calls run(), which in turn calls gameLoop().
  //gameThread.start();
}

// Run the game loop here.
private void gameLoop() {
  // Regenerate the game objects for a new game
  // ......
  //state = State.PLAYING;

  // Game loop
  ActionListener al = new ActionListener() {

      long beginTime, timeTaken, timeLeft;

      public void actionPerformed(ActionEvent ae) {
         beginTime = System.nanoTime();
         if (state == gameState.DISCONNECTED) {
             //break;  // break the loop to finish the current play
             System.out.println("do SOMETHING here..");
         }
         if (state == gameState.PLAYING) {
            // Update the state and position of all the game objects,
            // detect collisions and provide responses.
            gameUpdate();
         }
         // Refresh the display
         repaint();
         // Delay timer to provide the necessary delay to meet the target rate
         timeTaken = System.nanoTime() - beginTime;
         timeLeft = (UPDATE_PERIOD - timeTaken) / 1000000L;  // in milliseconds
         if (timeLeft < 10) timeLeft = 10;   // set a minimum
     }
 };
 timer = new Timer(40,al);
 timer.start();
}

// Update the state and position of all the game objects,
// detect collisions and provide responses.
public void gameUpdate() {

}

// Refresh the display. Called back via rapaint(), which invoke the paintComponent().
private void gameDraw(Graphics2D g2d) {
  switch (state) {
     case INITIALIZED:
    g2d.setColor (Color.red);
    g2d.drawString ("init",20,20);
    break;
     case PLAYING:
    g2d.setColor (Color.red);
    g2d.drawString ("play",20,20);
    break;
  case CONNECTING:
    g2d.setColor (Color.red);
    g2d.drawString ("connecting",20,20);
    break;
  case DISCONNECTED:
    g2d.setColor (Color.red);
    g2d.drawString ("disconnect",20,20);
    break;
  }

  g2d.setColor (Color.GREEN);
  g2d.drawString ("Re-paint: " + DRAWS,30,30);
  this.DRAWS++;
  // ......
}

// Process a key-pressed event. Update the current state.
public void gameKeyPressed(int keyCode) {
  switch (keyCode) {
     case KeyEvent.VK_UP:
        // ......
        break;
     case KeyEvent.VK_DOWN:
        // ......
        break;
     case KeyEvent.VK_LEFT:
        // ......
        break;
     case KeyEvent.VK_RIGHT:
        // ......
        break;
  }
}

// Process a key-released event.
public void gameKeyReleased(int keyCode) {  }

// Process a key-typed event.
public void gameKeyTyped(char keyChar) {  }

// Other methods
// ......

// Custom drawing panel, written as an inner class.
class GameCanvas extends JPanel implements KeyListener {
  // Constructor
  public GameCanvas() {
     setFocusable(true);  // so that can receive key-events
     requestFocus();
     addKeyListener(this);
  }

  // Override paintComponent to do custom drawing.
  // Called back by repaint().
  @Override
  public void paintComponent(Graphics g) {
     Graphics2D g2d = (Graphics2D)g;
     super.paintComponent(g2d);   // paint background
     setBackground(Color.BLACK);  // may use an image for background

     // Draw the game objects
     gameDraw(g2d);
  }

  // KeyEvent handlers
  @Override
  public void keyPressed(KeyEvent e) {
     gameKeyPressed(e.getKeyCode());
  }

  @Override
  public void keyReleased(KeyEvent e) {
     gameKeyReleased(e.getKeyCode());
  }

  @Override
  public void keyTyped(KeyEvent e) {
     gameKeyTyped(e.getKeyChar());
  }
}
}

Note that the addition of the single line comment at the top of the source means that (once compiled) it can be launched from the command line using..

> appletviewer GameApplet.java 
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433