Reputation: 97
I am working on a game. When trying to visualize using a game-loop in a "main"-Thread the paintComponent
method is only called scarcely. I've read up on many other similar problems, but none seem to help me. Could be, that I still don't understand everything correctly.
I know it is still an issue with the EDT as when I move my mouse or press a key the game is rendered smoothly.
Thank you!
Here my shortened code:
public class Game implements Runnable {
private GamePanel gamePanel;
private Thread gameThread;
private final int FPS_SET = 120;
public Game() {
gamePanel = new GamePanel();
gamePanel.requestFocus();
gameThread = new Thread(this);
gameThread.start();
}
@Override
public void run() {
double timePerFrame = 1000000000.0 / FPS_SET ;
long lastFrame = System.nanoTime();
long now = lastFrame;
while(true) {
now = System.nanoTime();
if (now - lastFrame >= timePerFrame) {
// Updates Scenery
_reDraw();
lastFrame = now;
}
}
}
private void _reDraw() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
gamePanel.repaint();
}
});
}
}
public class GamePanel extends JPanel {
// ...
public void paintComponent(Graphics g) {
// draw
}
}
Upvotes: 1
Views: 71
Reputation: 36441
This answer will not tell you how to solve your problem as @VGR already told you the way to go. This is a tentative explanation on the problem you faced.
From repaint()
doc:
If this component is a lightweight component, this method causes a call to this component's
paint
method as soon as possible. Otherwise, this method causes a call to this component'supdate
method as soon as possible.
As soon as possible do not mean that each time you call repaint()
paint
will be called. You may call repaint()
faster than the toolkit is able to answer your request.
From the doc again:
App-triggered Painting
In an application-triggered painting operation, the component decides it needs to update its contents because its internal state has changed. (For example,. a button detects that a mouse button has been pressed and determines that it needs to paint a "depressed" button visual).
and :
The Paint Method
Regardless of how a paint request is triggered, the AWT uses a "callback" mechanism for painting, and this mechanism is the same for both heavyweight and lightweight components. This means that a program should place the component's rendering code inside a particular overridden method, and the toolkit will invoke this method when it's time to paint.
the toolkit will invoke this method when it's time to paint is the important part.
and:
App-triggered painting
An app-triggered painting operation takes place as follows:
The program determines that either part or all of a component needs to be repainted in response to some internal state change.
The program invokes repaint() on the component, which registers an asynchronous request to the AWT that this component needs to be repainted.
The AWT causes the event dispatching thread to invoke update() on the component.
NOTE: If multiple calls to repaint() occur on a component before the initial repaint request is processed, the multiple requests may be collapsed into a single call to update(). The algorithm for determining when multiple requests should be collapsed is implementation-dependent. If multiple requests are collapsed, the resulting update rectangle will be equal to the union of the rectangles contained in the collapsed requests.
Real painting is not on your side, it's on the host UI side. There may be many events that do not permit the painting to take place (your window is not visible, there is more urgent things to do, etc).
At least, we known that for a repaint
there will be a paint
in the
future, but several repaint
may lead to a single paint
.
Note: there is no need to trigger repaint
on the ui-thread (via invokeLater
). You just have to call it directly from your thread as it pushes the request to later paint
on the ui-thread. Prefer:
private void _reDraw() {
gamePanel.repaint();
}
Upvotes: 1