Hi,
I was reading your postings since I wanted to use Swing as data input framework for my game so that I don’t need to develop one of my own.
Here I send you a little test program which open a dialog for text input and it seems to work OK. Don’t know if it represent the kind of problems you have with your rendering code.
I followed the active rendering examples from the book Killer Game Programming it seems to be deadlock free (can’t assure it though).
The problem I found with this is that it seems to me that the code is somewhat slow and the cube doesn’t get rendered solid but I shows some flickering. So I post the code to see if anyone has a better idea of why those things are happening.
Regards,
Gabriel
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import com.sun.j3d.utils.timer.*;
public class ActiveSwingTest implements Runnable {
static final int VEL = 1;
// Number of frames with a delay of 0 ms before the animation thread yields
// to other running threads.
private static final int NO_DELAYS_PER_YIELD = 16;
// no. of frames that can be skipped in any one animation loop
// i.e the games state is updated but not rendered
private static int MAX_FRAME_SKIPS = 5; // was 2;
JFrame f;
JPanel panel;
Image backBuffer;
JDialog dialog;
private long gameStartTime;
private long prevStatsTime;
private boolean running;
private Graphics2D graphics;
private long period;
private long framesSkipped = 0;
int x = 0;
int y = 0;
int vx = VEL;
int vy = VEL;
public ActiveSwingTest() {
initGraphics();
}
public void initGraphics() {
panel = new JPanel();
panel.setPreferredSize(new Dimension(800, 600));
panel.setFocusable(true);
panel.requestFocus();
panel.setIgnoreRepaint(true);
readyForTermination();
f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(panel);
f.setIgnoreRepaint(true);
f.setResizable(false);
f.pack();
backBuffer = createBackBuffer();
if(backBuffer == null) {
return;
}
f.setVisible(true);
}
public void run() {
long timeDiff = 0;
long overSleepTime = 0L;
int noDelays = 0;
long excess = 0L;
gameStartTime = J3DTimer.getValue();
prevStatsTime = gameStartTime;
long beforeTime = gameStartTime;
running = true;
graphics = (Graphics2D) backBuffer.getGraphics();
while(running) {
update(timeDiff);
render(graphics);
paintScreen();
long afterTime = J3DTimer.getValue();
timeDiff = afterTime - beforeTime;
long sleepTime = (period - timeDiff) - overSleepTime;
if(sleepTime > 0) { // some time left in this cycle
try {
Thread.sleep(sleepTime / 1000000L); // nano -> ms
}
catch(InterruptedException ex) {}
overSleepTime = (J3DTimer.getValue() - afterTime) - sleepTime;
}
else { // sleepTime <= 0; the frame took longer than the period
excess -= sleepTime; // store excess time value
overSleepTime = 0L;
if(++noDelays >= NO_DELAYS_PER_YIELD) {
Thread.yield(); // give another thread a chance to run
noDelays = 0;
}
}
beforeTime = J3DTimer.getValue();
/* If frame animation is taking too long, update the game state
without rendering it, to get the updates/sec nearer to
the required FPS. */
int skips = 0;
while((excess > period) && (skips < MAX_FRAME_SKIPS)) {
excess -= period;
update(timeDiff); // update state but don't render
skips++;
}
framesSkipped += skips;
}
System.exit(0); // so window disappears
}
private void showDialogo() {
if ( dialog == null ) {
dialog = new JDialog(f, "Example dialog", true);
final JTextField t = new JTextField("hello");
JButton bok = new JButton("OK");
bok.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("text="+t.getText());
dialog.setVisible(false);
dialog.dispose();
dialog = null;
}
});
JButton bcancel = new JButton("Cancel");
bcancel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dialog.setVisible(false);
}
});
final Container c = dialog.getContentPane();
c.setLayout(new BorderLayout());
c.add(t, BorderLayout.CENTER);
final JPanel buttonPanel = new JPanel();
buttonPanel.add(bok);
buttonPanel.add(bcancel);
c.add(buttonPanel, BorderLayout.PAGE_END);
dialog.pack();
dialog.setLocationRelativeTo(f);
dialog.setVisible(true);
}
else {
dialog.setVisible(true);
}
}
private void paintScreen() {
// use active rendering to put the buffered image on-screen
try {
final Graphics g = panel.getGraphics();
if(g != null) {
g.drawImage(backBuffer, 0, 0, null);
}
g.dispose();
}
catch(Exception e) {
System.out.println("Graphics context error: " + e);
}
}
private Image createBackBuffer() {
final Image dbImage = panel.createImage(800, 600);
if(dbImage == null) {
System.out.println("could not create the backbuffer image!");
}
return dbImage;
}
private void readyForTermination() {
panel.addKeyListener(new KeyAdapter() {
// listen for esc, q, end, ctrl-c on the canvas to
// allow a convenient exit from the full screen configuration
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if((keyCode == KeyEvent.VK_ESCAPE) || (keyCode == KeyEvent.VK_Q) ||
(keyCode == KeyEvent.VK_END) ||
((keyCode == KeyEvent.VK_C) && e.isControlDown())) {
running = false;
}
else if ( keyCode == KeyEvent.VK_D ) {
showDialogo();
}
}
});
}
private void update(long dt) {
x += vx;
y += vy;
if ( x < 0 ) {
x = 0;
vx = VEL;
}
else if ( x > 700 ) {
x = 700;
vx = -VEL;
}
if ( y < 0 ) {
y = 0;
vy = VEL;
}
else if ( y > 500 ) {
y = 500;
vy = -VEL;
}
}
private void render(Graphics2D g) {
g.setColor(Color.RED);
g.fillRect(0, 0, 800, 600);
g.setColor(Color.WHITE);
g.fillRect(x, y, 100, 100);
}
public static void main(String[] args) {
ActiveSwingTest test = new ActiveSwingTest();
new Thread(test).start();
}
}