5
\$\begingroup\$

I am trying to create a frame independent game loop.

I am currently using the following code

 private class MainGameThread extends Thread {
        public static final int PAUSE_SLEEP_TIME = 10;
        long previous = getCurrentTimeInMillis();
        long totalElapsed = 0;
        long gameTimeStart = 0;
        long currentTime = getCurrentTimeInMillis();
        long accumulatorTime = 0;

        @Override
        public void run() {
            gameTimeStart = getCurrentTimeInMillis();
            while (mIsRunning) {
                // Pause game
                while (mIsPaused) {
                    try {
                        Thread.sleep(PAUSE_SLEEP_TIME);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                long current = getCurrentTimeInMillis();
                long elapsed = current - previous;
                previous = current;
                processGameInput();
                updateGameState(elapsed);
                totalElapsed += elapsed;
                if (totalElapsed > MainGame.MS_PER_FRAME) {
                    drawGame();
                    totalElapsed -= MainGame.MS_PER_FRAME;
                    if (totalElapsed > MainGame.MS_PER_FRAME) {
                        Log.e("GAME", "Performance warning, rendering or update took too long");
                    }
                }
                checkIfGameShouldStop(gameTimeStart);
            }
            shutdownGracefully();
        }

        private void shutdownGracefully() {
        }
    }

It works, however it performs differently on different devices.

In my game models update method I am performing the following actions

  mCenterX += (mVelocityVector.x * timeElapsed);
  mCenterY += (mVelocityVector.y * timeElapsed + gameSpeed.getValue())

I have read this article, but I can't understand the final example:

double t = 0.0;
double dt = 0.01;

double currentTime = hires_time_in_seconds();
double accumulator = 0.0;

State previous;
State current;

while ( !quit )
{
    double newTime = time();
    double frameTime = newTime - currentTime;
    if ( frameTime > 0.25 )
        frameTime = 0.25;
    currentTime = newTime;

    accumulator += frameTime;

    while ( accumulator >= dt )
    {
        previousState = currentState;
        integrate( currentState, t, dt );
        t += dt;
        accumulator -= dt;
    }

    const double alpha = accumulator / dt;

    State state = currentState * alpha + 
        previousState * ( 1.0 - alpha );

    render( state );
}

What does the State class object mean in this case?

Could someone please help to adapt this example to my case or explain the example from the article and how I could update my loop to be frame independent?

\$\endgroup\$
3
\$\begingroup\$

The State is meant to represent all of the data your program needs to render the current scene. So mCenterX and mCenterY in your example, (plus whatever else you might be drawing.) There's not a strict need for it to all live inside one class, although I have found it useful to do so in the past.

The article is suggesting that you linearly interpolate or "lerp" the current state with the previous state, (that is, the one most recently calculated, which may not be the one that was shown last.) You'll have to define your own lerp method for the class or classes that hold your state, usually delegating to the one for the members of your class. Non-number values like booleans and enums do not need to be lerped, you can just use the newest values.

The article explains why you need to lerp, if you don't want jerky motion in some cases:

...consider a situation where the display framerate is 60fps and the physics is running at 50fps. There is no nice multiple so the accumulator causes the simulation to alternate between mostly taking one and occasionally two physics steps per-frame when the remainders “accumulate” above dt.

So in those cases where two physics steps happen per frame, a moving object would appear to jump farther than it should. So instead we lerp, placing the object between the two states, based on how much time has elapsed.

Hopefully now you understand enough of the idea adjust your code yourself. You'll have to decide how you want to arrange your equivalent of the State class.

Further reading:This book chapter also compares different game loop methods including the one mentioned above.

\$\endgroup\$
  • \$\begingroup\$ there are far better methods, I'm trying to find the article I looked at before, but pure delta time systems are not scalable and are prone to bugs, scheduled calculations work better especially for lower end systems, where you cap off the max computation time on your game creating reliable system requirements and managing performance better. \$\endgroup\$ – opa Jan 17 '18 at 21:12
  • \$\begingroup\$ @snb My answer was mainly trying to explain the linked article. This book chapter is the best comparison of different gameloop methods I'm aware of, and it mentions the linked article in an extremely positive light. The chapter does mention that the method above, (the last one in the list,) is the most complicated. But I'm not sure where a scaling concern would come from since most games have only one game loop, and once the loop is set up, in my experience, you rarely touch it again. I'd be interested to see a better method though! \$\endgroup\$ – Ryan1729 Jan 18 '18 at 3:15
  • \$\begingroup\$ I made a mistake, I didn't see that OP linked to something in his post, and his article was the one I was talking about. I thought you were talking about delta timing instead of interpolating between updates. If you include that book chapter link in your article I can upvote your post. \$\endgroup\$ – opa Jan 18 '18 at 15:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.