BlackWolf
BlackWolf

Reputation: 5599

Does "deWiTTERS Game Loop" assume a constant UPS?

I recently tried to get into games programming. I am pretty experienced with Java, but not with game programming. I read http://www.koonsolo.com/news/dewitters-gameloop/ and implemented the game loop proposed there with the following code:

private static int UPDATES_PER_SECOND = 25;
private static int UPDATE_INTERVAL = 1000 / UPDATES_PER_SECOND * 1000000;
private static int MAX_FRAMESKIP = 5;

public void run() {
    while (true) {
        int skippedFrames = 0;
        while (System.nanoTime() > this.nextUpdate && skippedFrames < MAX_FRAMESKIP) {
            this.updateGame();
            this.nextUpdate += UPDATE_INTERVAL;
            skippedFrames++;
        }

        long currentNanoTime = System.nanoTime();
        double interpolation = (currentNanoTime + UPDATE_INTERVAL - this.nextUpdate) / UPDATE_INTERVAL;
        this.repaintGame(interpolation);
    }
}

The loop looked promising and easy, but now that I am actually trying to do something with it I am not so sure anymore. If I am not completly mistaken updateGame() takes care of things like calculating positions, moving enemies, calculating collisions, ...? Since the interpolation is not passed to updateGame(), does that mean that we assume that updateGame() is called exactly and steadily UPDATES_PER_SECOND times per second? Does that mean all our calculations are based on that assumption? And wouldn't that cause us a lot of trouble if - for whatever reason - the call to updateGame() is delayed?
For example, if my character sprite is supposed to walk right and we move it according to its speed on every updateGame() - if the method were to be delayed, that would mean our calculations are simply off and the character would lag?

On the website, the following example for the interpolation is stated:

If in the 10Th gametick the position is 500, and the speed is 100, then in the 11Th gametick the position will be 600. So where will you place your car when you render it? You could just take the position of the last gametick (in this case 500). But a better way is to predict where the car would be at exact 10.3, and this happens like this:
view_position = position + (speed * interpolation)
The car will then be rendered at position 530.

I know it's just an example, but wouldn't that make the car speed dependent on the UPDATES_PER_SECOND? So more UPS would mean a faster car? This can't be right...?

Any help, tutorial, whatever is appreciated.

UPDATE / SOLUTION

After all the help (thanks!), here is what I am currently using - works pretty good so far (for moving a character sprite), but let's wait for the really complicated game stuff to decide this. Still, I wanted to share this.
My game loop now looks like this:

private static int UPDATES_PER_SECOND = 25;
private static int UPDATE_INTERVAL = 1000 / UPDATES_PER_SECOND * 1000000;
private static int MAX_FRAMESKIP = 5;

private long nextUpdate = System.nanoTime();

public void run() {
    while (true) {
        int skippedFrames = 0;
        while (System.nanoTime() > this.nextUpdate && skippedFrames < MAX_FRAMESKIP) {
            long delta = UPDATE_INTERVAL;
            this.currentState = this.createGameState(delta);
            this.newPredictedNextState = this.createGameState(delta + UPDATE_INTERVAL, true);

            this.nextUpdate += UPDATE_INTERVAL;
            skippedFrames++;
        }

        double interpolation = (System.nanoTime() + UPDATE_INTERVAL - this.nextUpdate) / (double) UPDATE_INTERVAL;
        this.repaintGame(interpolation);
    }
}

As you can see, a few changes:

Maybe for clarification, an example. Here is how I calculate the character position (simplified a little):

public GameState createGameState(long delta, boolean ignoreNewInput) {
    //Handle User Input and stuff if ignoreNewInput=false

    GameState newState = this.currentState.copy();
    Sprite charSprite = newState.getCharacterSprite();
    charSprite.moveByX(charSprite.getMaxSpeed() * delta * charSprite.getMoveDirection().getX()); 
    //getMoveDirection().getX() is 1.0 when the right arrow key is pressed, otherwise 0.0
}

... then, in the paint method of the window ...

public void paint(Graphics g) {
    super.paint(g);

    Graphics2D g2d = (Graphics2D) g;

    Sprite currentCharSprite = currentGameState.getCharacterSprite();
    Sprite nextCharSprite = predictedNextState.getCharacterSprite(); 
    Position currentPos = currentCharSprite.getPosition();
    Position nextPos = nextCharSprite.getPosition();
    //Interpolate position
    double x = currentPos.getX() + (nextPos.getX() - currentPos.getX()) * this.currentInterpolation;
    double y = currentPos.getY() + (nextPos.getY() - currentPos.getY()) * this.currentInterpolation;
    Position interpolatedCharPos = new Position(x, y);
    g2d.drawImage(currentCharSprite.getImage(), (int) interpolatedCharPos.getX(), (int) interpolatedCharPos.getY(), null);
}

Upvotes: 8

Views: 2390

Answers (1)

Lucius
Lucius

Reputation: 3745

Don't base your game logic on the assumption that the update intervals are constant. Include a game clock that exactly measures the time that passed between two updates. You can then base all your calculations on the delay and don't have to worry about the actual update rate.

In this case the cars velocity would be given in units/second and the delta would be the total seconds since the last update:

car.position += car.velocity * delta;

It's a common practice to separate updating the game logic from drawing the frame. Like you said, this makes it possible to keep a steady update rate by skipping rendering a frame every now and then.

But it's not so much about keeping the update intervals constant. It is just really important to have a minimum number of updates per time unit. Imagine a vehicle moving really fast towards an obstactle. If the update frequency is too low, the travelled distance between two updates could be greater than the obstacles total size. The vehicle would move right through it.

Upvotes: 4

Related Questions