Search code examples
javacpu-usagegame-loop

Java: Game loop - CPU usage is too high


I try to write my own game loop. But the cpu usage is too high. It's about 25%. My problem could be that there is no Thread.sleep. But I don't want to use it beacuse it should be not very accurate. Also calculating the next frame could need 10 ms.

My code is doing this:
1: repaint the window ('fenster' is window in german)
2: read out the milliseconds
3: set up the next frame
I wanna have 50 fps. So the program need to wait 1000/50 = 20 ms every frame.
4: Calculate the difference of the time for and after setting up the next frame

        // GameSchleife
        while (true) {

            // First repaint
            fenster.repaint();

            // Before calculating the next frame, capture the time
            long a = System.currentTimeMillis();
            long b = a;

            // Calculate the next frame
            fenster.Update();

            // Wait for 20 ms (50 frames in a second)
            for (int time = 0; time < 20;) {

                // Wait for at least 1 ms
                while (a == b) {
                    a = System.currentTimeMillis();
                }

                // Difference from a and b
                time = time + ((int) (a - b));

                // 
                a = System.currentTimeMillis();
                b = a;

            }
        }

Solution

  • Why would Thread.sleep be less accurate than your code? Have you tested it and found it inaccurate? If you don't want to sleep for 20ms, split the sleep in several parts, adjusting the final sleep so the total is 20ms.

    Something like this: (I'm a C# dev so you probably have to adjust the code a little)

    while (true)
    {
        long startTime = System.currentTimeMillis();
    
        fenster.repaint();
    
        long remaining= 20 - (System.currentTimeMillis() - startTime);
        Thread.sleep(remaining - 2); // adds up to 18 ms since start if repaint took 10ms
    
        remaining= 20 - (System.currentTimeMillis() - startTime);
        Thread.sleep(remaining); // should be 1-2ms
    }
    

    The code you have now will kill any laptop battery in no time, and possible give you other heating issues.

    Edit: Added fenster.repaint(). Also fixed the code so that repaint + wait totals 20ms. This code will break if repaint takes more than 18ms, so add some tests.

    Another edit to clarify: Your frame takes 10ms to paint. To achieve 50 fps, you have a total of 20ms per frame, ie if the repaint took 11ms, you must sleep 9ms before painting the next frame.

    startTime is assigned first, followed by repaint which take appx 10ms. After the repaint, we check the system timer again, and subtract the start time to get the elapsed time. We then subtract this number from 20 to get the number of ms until the next repaint.

    Then we enter sleep, but subtract 2ms to have a little margin in case the system timer is inaccurate. Again we recalculate remaining time (1-2ms) and enter another short sleep.

    All this adds up to 20ms, and you can render your next frame.