I am writting a simple game, and I want to cap my framerate at 60 fps without making the loop eat my cpu. How would I do this?
-
4Don't hardcode 60 FPS, determine the actual refresh rate of the display. 60 FPS will be jerky on e.g. a 85 Hz monitor. – Ben Voigt Sep 25 '12 at 00:07
10 Answers
You can read the Game Loop Article. It's very important that you first understand the different methodologies for the game loop before trying to implement anything.
-
6
-
@Feelsbadman Use web archive https://web.archive.org/web/20160701000000*/http://www.koonsolo.com/news/dewitters-gameloop/ – Argamidon Oct 13 '21 at 19:25
I took the Game Loop Article that @cherouvim posted, and I took the "Best" strategy and attempted to rewrite it for a java Runnable, seems to be working for me
double interpolation = 0;
final int TICKS_PER_SECOND = 25;
final int SKIP_TICKS = 1000 / TICKS_PER_SECOND;
final int MAX_FRAMESKIP = 5;
@Override
public void run() {
double next_game_tick = System.currentTimeMillis();
int loops;
while (true) {
loops = 0;
while (System.currentTimeMillis() > next_game_tick
&& loops < MAX_FRAMESKIP) {
update_game();
next_game_tick += SKIP_TICKS;
loops++;
}
interpolation = (System.currentTimeMillis() + SKIP_TICKS - next_game_tick
/ (double) SKIP_TICKS);
display_game(interpolation);
}
}

- 2,555
- 5
- 34
- 69

- 2,712
- 1
- 23
- 31
-
1It is the skeleton of the better way to do a game loop, but it doesn't explain much does it? Maybe add a few short bullet points explaining things? – TT. Jan 09 '16 at 08:00
-
This is somewhat how I do it. Ok, so we loop, and if the time past between loop iterations is less than skip, we just loop again. If skip or more time has passed, we call the main game engine and ask for the next frame. I like to pull the whole frame as an image and send that to the paint method. In my game class, I like to calculate max frame per second in case player wants to show actual FPS, which when I set max fps, the actual is always a few frames less than set amount, ie set at 60 actual is 57. – Jon Crawford Dec 10 '16 at 17:37
The simple answer is to set a java.util.Timer to fire every 17 ms and do your work in the timer event.

- 63,018
- 25
- 139
- 189
-
1What if it takes more than 17ms to finish the work in the timer event? – cherouvim May 30 '11 at 06:23
-
1@cherouvim: Then you are overloaded and your framerate drops. You are still doing work as fast as possible, but no faster than 60 fps. – Lawrence Dol May 30 '11 at 06:34
Here's how I did it in C++... I'm sure you can adapt it.
void capFrameRate(double fps) {
static double start = 0, diff, wait;
wait = 1 / fps;
diff = glfwGetTime() - start;
if (diff < wait) {
glfwSleep(wait - diff);
}
start = glfwGetTime();
}
Just call it with capFrameRate(60)
once per loop. It will sleep, so it doesn't waste precious cycles. glfwGetTime()
returns the time since the program started in seconds... I'm sure you can find an equivalent in Java somewhere.

- 272,448
- 266
- 850
- 1,236
-
1In Java, the equivalent of glfwGetTime() would be System.currentTimeMillis(). It returns the time in milliseconds since January 1st, 1970. When used like this, it does about the same thing except it returns the time in milliseconds. – tobahhh Sep 02 '16 at 13:21
Here's a tip for using Swing and getting reasonably accurate FPS without using a Timer.
Firstly don't run the game loop in the event dispatcher thread, it should run in its own thread doing the hard work and not getting in the way of the UI.
The Swing component displaying the game should draw itself overriding this method:
@Override
public void paintComponent(Graphics g){
// draw stuff
}
The catch is that the game loop doesn't know when the event dispatcher thread has actually got round to performing the paint action because in the game loop we just call component.repaint() which requests a paint only that may happen later.
Using wait/notify you can get the redraw method to wait until the requested paint has happened and then continue.
Here's complete working code from https://github.com/davidmoten/game-exploration that does a simple movement of a string across a JFrame using the method above:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
* Self contained demo swing game for stackoverflow frame rate question.
*
* @author dave
*
*/
public class MyGame {
private static final long REFRESH_INTERVAL_MS = 17;
private final Object redrawLock = new Object();
private Component component;
private volatile boolean keepGoing = true;
private Image imageBuffer;
public void start(Component component) {
this.component = component;
imageBuffer = component.createImage(component.getWidth(),
component.getHeight());
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
runGameLoop();
}
});
thread.start();
}
public void stop() {
keepGoing = false;
}
private void runGameLoop() {
// update the game repeatedly
while (keepGoing) {
long durationMs = redraw();
try {
Thread.sleep(Math.max(0, REFRESH_INTERVAL_MS - durationMs));
} catch (InterruptedException e) {
}
}
}
private long redraw() {
long t = System.currentTimeMillis();
// At this point perform changes to the model that the component will
// redraw
updateModel();
// draw the model state to a buffered image which will get
// painted by component.paint().
drawModelToImageBuffer();
// asynchronously signals the paint to happen in the awt event
// dispatcher thread
component.repaint();
// use a lock here that is only released once the paintComponent
// has happened so that we know exactly when the paint was completed and
// thus know how long to pause till the next redraw.
waitForPaint();
// return time taken to do redraw in ms
return System.currentTimeMillis() - t;
}
private void updateModel() {
// do stuff here to the model based on time
}
private void drawModelToImageBuffer() {
drawModel(imageBuffer.getGraphics());
}
private int x = 50;
private int y = 50;
private void drawModel(Graphics g) {
g.setColor(component.getBackground());
g.fillRect(0, 0, component.getWidth(), component.getHeight());
g.setColor(component.getForeground());
g.drawString("Hi", x++, y++);
}
private void waitForPaint() {
try {
synchronized (redrawLock) {
redrawLock.wait();
}
} catch (InterruptedException e) {
}
}
private void resume() {
synchronized (redrawLock) {
redrawLock.notify();
}
}
public void paint(Graphics g) {
// paint the buffered image to the graphics
g.drawImage(imageBuffer, 0, 0, component);
// resume the game loop
resume();
}
public static class MyComponent extends JPanel {
private final MyGame game;
public MyComponent(MyGame game) {
this.game = game;
}
@Override
protected void paintComponent(Graphics g) {
game.paint(g);
}
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
MyGame game = new MyGame();
MyComponent component = new MyComponent(game);
JFrame frame = new JFrame();
// put the component in a frame
frame.setTitle("Demo");
frame.setSize(800, 600);
frame.setLayout(new BorderLayout());
frame.getContentPane().add(component, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
game.start(component);
}
});
}
}

- 11,957
- 2
- 40
- 47
-
1I think any game loop that uses [Thread.Sleep](https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#sleep%28long,%20int%29) is flawed. From the docu: `Causes the currently executing thread to sleep [...] for the specified number of milliseconds plus the specified number of nanoseconds, **subject to the precision and accuracy of system timers and schedulers**.`. There is no guarantee that the thread will actually sleep for the number of milliseconds+nanos you ask for. – TT. Jan 09 '16 at 07:51
-
I agree actually that using `Thread.sleep` is not ideal. Non-blocking techniques would be nicer. I'm not sure you can do any better that the accuracy `Thread.sleep` offers with a non-blocking technique though using a `ScheduledExecutorService` say. What do you think? – Dave Moten Jan 09 '16 at 10:38
-
IMO the best is a loop similar to the one posted by Parth Mehrotra, ie one that doesn't rely on sleeping or something similar. – TT. Jan 09 '16 at 10:41
-
Yep probably would be more accurate but you dedicate a processor to it. Depends on the game I suppose. – Dave Moten Jan 09 '16 at 11:03
-
Indeed. I must admit I have never developed a game, so I'm not entirely sure what the best way is. I just think relying on `Thread.Sleep` is probably iffy. – TT. Jan 09 '16 at 11:11
-
I think you could find an answer on [gamedev.stackexchange.com](https://gamedev.stackexchange.com/). – TT. Jan 11 '16 at 09:42
There is a lot of good information here already, but I wanted to share one problem that I had with these solutions, and the solution to that. If, despite all these solutions, your game/window seems to skip (especially under X11), try calling Toolkit.getDefaultToolkit().sync()
once per frame.

- 1,802
- 3
- 17
- 26
-
Thanks I had an issue with a lot of dropped frames (rendered but not shown) and this solved the problem. – Jonathan Apr 05 '20 at 10:45
Timer is inaccurate for controlling FPS in java. I have found that out second hand. You will need to implement your own timer or do real time FPS with limitations. But do not use Timer as it's not 100% accurate, and therefore cannot execute tasks properly.

- 71
- 3
In java you could do System.currentTimeMillis()
to get the time in milliseconds instead of glfwGetTime()
.
Thread.sleep(time in milliseconds)
makes the thread wait in case you don't know, and it must be within a try
block.

- 2,336
- 5
- 31
- 40
What I have done is to just continue to loop through, and keep track of when I last did an animation. If it has been at least 17 ms then go through the animation sequence.
This way I could check for any user inputs, and turn musical notes on/off as needed.
But, this was in a program to help teach music to children and my application was hogging up the computer in that it was fullscreen, so it didn't play well with others.

- 41,583
- 10
- 86
- 166
If you are using Java Swing the simplest way to achieve a 60 fps is by setting up a javax.swing.Timer like this and is a common way to make games:
public static int DELAY = 1000/60;
Timer timer = new Timer(DELAY,new ActionListener() {
public void actionPerformed(ActionEvent event)
{
updateModel();
repaintScreen();
}
});
And somewhere in your code you must set this timer to repeat and start it:
timer.setRepeats(true);
timer.start();
Each second has 1000 ms and by dividing this with your fps (60) and setting up the timer with this delay (1000/60 = 16 ms rounded down) you will get a somewhat fixed framerate. I say "somewhat" because this depends heavily on what you do in the updateModel() and repaintScreen() calls above.
To get more predictable results, try to time the two calls in the timer and make sure they finish within 16 ms to uphold 60 fps. With smart preallocation of memory, object reuse and other things you can reduce the impact of the Garbage Collector also. But that is for another question.

- 1,211
- 1
- 12
- 13
-
1The problem with timers is comparable to that of using Thread.Sleep... there is no guarantee the delay is exact. Generally the actual DELAY is unpredictable and will possibly be slightly longer than what you passed as DELAY parameter. – TT. Jan 09 '16 at 08:07