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?
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.
The simple answer is to set a java.util.Timer to fire every 17 ms and do your work in the timer event.
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.
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.
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.
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.