1
\$\begingroup\$

I had an old pure Java 2D top-down game that I'm trying to reformat a bit. Before, I had a bit of a messy game loop that didn't use delta timing for any movements or animations. Now, I'm trying to implement this delta timing.

I implemented delta timing into the game loop with two framerates (60FPS and 30FPS) to test it, but I noticed that the movement created with the 30FPS was half of what was obtained using the 60FPS.

When I moved the player, I did something like:

posX += Game.deltaTime * SPEED;

and this didn't work well because the delta time was the same for both the 30FPS and 60FPS.

So, I did some calculations... I run into some problems if I use change the player's position from the update() method, because it runs at a constant 60UPS.

Here are my calculations


int deltaXPerSecond = Game.deltaTime() * SPEED * 60;

// To get the delta X over a second, I multiplied deltaX by 60 (UPS)
// with 30FPS, it was 9.9, with 200FPS it was 1.5

// Game Loop

private static final double UPDATES_PER_SECOND = 1.0 / 60.0;

@Override
        public void run() {
            
            /*
             * Note: This game loop uses Majoolwip's game loop.
             * https://www.youtube.com/watch?v=4iPEjFUZNsw&list=PL7dwpoQd3a8j6C9p5LqHzYFSkii6iWPZF
             */
            
            double firstTime = 0;
            // converts to seconds because System.nanoTime is extremely accurate.
            double lastTime = System.nanoTime() / 1000000000.0;
            double passedTime = 0;
            double unprocessedTime = 0;
            
            double frameTime = 0;
            int frames = 0;
            int fps = 0;
            
            boolean render = false;
            
            // Continuously repeats this loop until the game is stopped.
            while (running) {
                render = false;
                firstTime = System.nanoTime() / 1000000000.0; // converts to seconds.
                
                // Gets how many milliseconds have passed between now and the last time.
                passedTime = firstTime - lastTime;
                deltaTime = (float) passedTime;
                lastTime = firstTime; 
                unprocessedTime += passedTime;
                frameTime += passedTime;
                
                while (unprocessedTime >= UPDATES_PER_SECOND) {
                    unprocessedTime -= UPDATES_PER_SECOND;
                    // Sets render to true. This makes the game render only when it updates
                    render = true;
                    
                    // Updates the game
                    if (frameTime >= 1.0) {
                        frameTime = 0;
                        fps = frames;
                        frames = 0;
                        System.out.println("FPS: " + fps);
                    }
                    update();
                }
                
                if (render) {
                    frames++;
                    render();
                }
                else {
                    
                    /*
                     * If the game doesn't need to render or update,
                     * the GameLoop thread sleeps for 1 millisecond,
                     * causing a decrease in CPU usage.
                     */
                    
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                
            }
        }

What am I doing wrong? Shouldn't these numbers be approximately the same regardless of the frame rate? Do I need to change the game loop? Please let me know what I am missing.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Hi @ElliottV4, welcome to GameDev. Are you just plain programming in Java, or rather using some kind of framework/library? It could help others to better frame your issue, and maybe propose a solution. \$\endgroup\$
    – liggiorgio
    Commented May 13, 2021 at 15:15
  • \$\begingroup\$ Just using plain Java at the moment. The only external library I am using is an audio player. \$\endgroup\$
    – ElliottV4
    Commented May 13, 2021 at 15:16
  • \$\begingroup\$ Update: I just realized that becuase player movement is physics related, shouldn't I run it at a fixed timestep? (eg. 60 updates per second) so the movement is constant? Would this be able to produce the same result on devices with different hardware? eg. high end i7 vs low end i5 processors? \$\endgroup\$
    – ElliottV4
    Commented May 14, 2021 at 17:29
  • \$\begingroup\$ Definitely yes, the result would be the same even at different screen refresh rates :) \$\endgroup\$
    – liggiorgio
    Commented May 14, 2021 at 21:25

1 Answer 1

1
\$\begingroup\$

Your player's movement, a crate falling off a cliff, and all physics-related simulations are basically (discrete) integrals computed over time. An object's position represents its displacement in a reference system; velocity is the rate of position change over time; acceleration is the rate of velocity change over time... We compute all these values by integrating at each update using very simple formulas like Euler's method.

If we want the output to be the same for any given input on any machine, we need to integrate over the same time interval at each loop. Therefore, we use a fixed timestep for our calculations; this value depends only on the number of updates per second we want. We also decouple logic and rendering, so we can deal with them separately: the renderer produces time, and the integrator consumes it in fixed-size chunks.

If your player behaves differently at different refresh rates, then your game loop still depends on the rendering rate, but we don't want this to happen. A game loop like this:

// ABSOLUTELY UNTESTED CODE
double t = 0.0;
final double dt = 1.0 / 60.0;  // game updates per second

double currentTime = System.nanoTime();
double accumulator = 0.0;

while (!quit) {
    double newTime = System.nanoTime();
    double frameTime = newTime - currentTime;
    currentTime = newTime;

    accumulator += frameTime / 1000000000.0;  // convert to seconds

    while (accumulator >= dt) {
        update();
        accumulator -= dt;
        t += dt;
    }

    render();
}

would call update() only if enough time has passed to make accumulator have at least one dt chunk. Additionally, if your application freezes this gets recorded during the next computation of frameTime: the inner loop will be executed multiple times until accumulator becomes smaller than dt once again.

Please note that dt is a fixed delta time, related to the game logic only. You can use it to move your objects around in their own update() function:

x += SPEED * dt;

This code is being executed as fast as possible, assuming that render operations are actually limited by the hardware. If you want to artificially limit the number of times render() is called, you can:

  • Couple it to the UPS rate with the same flag trick in your sample code;
  • Duplicate all variables to manage game loop and render loop separately (I actually see no reason to force a game run at 53 FPS though).

Glenn Fiedler wrote some articles about integration and timestep I absolutely loved, which helped me to improve my understanding of game engines and be aware of when certain instructions are to be called when programming a game. The full game physics series is available here, I hope you'll appreciate the reading too.

\$\endgroup\$
1
  • \$\begingroup\$ Thank you so much!! This was exactly what I was looking for. I’ll be sure to try and implement it into my game to see if the results will have a noticeable change. Thanks again :) \$\endgroup\$
    – ElliottV4
    Commented May 14, 2021 at 22:17

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .