I am using a Swing Timer in my game but when the game is running it appears to have moments when it runs smoothly and moments when it slows down.
Why is the time fl
The Swing Timer
is notorious for its inaccuracy. Use something else instead.
On prompting, I've decided to undelete this post. OTOH most of the extra information that makes it worth reinstating comes from trashgod, so I'll merely quote/paraphrase their comments.
I'd argue that it's reasonably accurate but easy to saturate.
And trashgod goes on to add in a separate comment that:
(I) might cite Clock Quality.
javax.swing.Timer
isn't very accurate, but it has usefully precise resolution on modern platforms.
In the panel constructor do:
setBackground(Color.RED);
Then you do not need to erase the background, as you are calling super.paintComponent.
In general calculate positions on actual time passed (System.nanoTime()) and do not rely on timer frames. There are a couple of gaming frameworks out there, so maybe it is worth looking at their solution. I liked the sample.
Because the rendering is trivial, I find this variation of your example to be very smooth. The render time is well below a half millisecond, so the 12 millisecond period (~83 Hz) is plenty of time to finish a frame, typically taking less that 10% of one core. As the render time grows, the timer thread becomes saturated, and events are coalesced. The effect is magnified on a single core, as rendering competes with garbage collection and external processing demands. Java is not a real-time system, and not all schedulers are created equal.
You'll certainly want to profile your actual code, as suggested here, to see any correlation with fluctuating performance. One alternative approach is to lengthen the period (decrease the frequency) to meet your rendering deadline and use a larger increment in moveImage()
to get the same velocity.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main extends JFrame {
private static final int W = 800;
private static final int H = 400;
public Main() {
super("JFrame");
this.add(new ImagePanel());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
setSize(W, H);
this.setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new Main();
}
});
}
class ImagePanel extends JPanel {
Timer movementTimer;
int x, y;
public ImagePanel() {
x = 0;
y = 0;
movementTimer = new Timer(12, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
moveImage();
repaint();
}
});
movementTimer.start();
}
public void moveImage() {
x++;
y++;
if (x > W) {
x = 0;
}
if (y > H) {
y = 0;
}
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
long start = System.nanoTime();
g.setColor(Color.RED);
g.fillRect(0, 0, W, H);
g.setColor(Color.BLUE);
g.fillRect(x, y, 50, 50);
double delta = (System.nanoTime() - start) / 1000000d;
g.drawString(String.format("%1$5.3f", delta), 5, 15);
}
}
}