Search code examples
javascriptgame-development2d-gamespixi.js

PixiJS Fixed-step loop - Stutter / Jitter


I've been facing a strange issue beyond my understanding of how to fix it.

I'm trying to create the following multiplayer game structure:

  • The server running at 20-30 fps
  • The client logic loop at the same FPS as the server
  • The client render loop

I'm using PixiJS for UI and here is where I got stuck. ( I've opened a thread here as well )

And I have a demo here: https://playcode.io/1045459

Ok, now let's explain the issue!

private update = () => {
    let elapsed = PIXI.Ticker.shared.elapsedMS
    if (elapsed > 1000) elapsed = this.frameDuration
    this.lag += elapsed

    //Update the frame if the lag counter is greater than or
    //equal to the frame duration
    while (this.lag >= this.frameDuration) {  

        //Update the logic
        console.log(`[Update] FPS ${Math.round(PIXI.Ticker.shared.FPS)}`)
        this.updateInputs(now())

        //Reduce the lag counter by the frame duration
        this.lag -= this.frameDuration
    }

    // Render elements in-between states
    const lagOffset = this.lag / this.frameDuration
    this.interpolateSpaceships(lagOffset)
}

In the client loop I keep track of both logic & render parts, limiting the logic one at 20FPS. It all works "cookies and clouds" until the browser has a sudden frame rate drop from 120fps to 60fps. Based on my investigation and a nice & confusing spreadsheet that I've put together when the frame rate drops, the "player" moves 2x more ( eg. 3.3 instead of 1.66 ) On paper it's normal and the math is correct, BUT this creates a small bounce / jitter / stutter or whatever naming this thing has.

In the demo that I've created in playcode it's not visible. My assumption is that the code is too basic and the framerate never drops.

Considering that the math and the algorithm are correct ( which I'm not yet sure ), I've turned my eyes to other parts that might affect this. I'm using pixi-viewport to follow the character. Could it be that the following part creates this bounce?

Does anyone have experience writing such a game loop?


Update: Okkkk, mindblowing result. I just found out that this happens even with the most simple version of the game loop ever. Just by multiplying x = x + speed * delta every frame.

For the same reason. Sudden drops in FPS.


Solution

  • Ok, I've found the solution. Will post it here as there is not a lot of info about it. The solution is to smooth out sudden fps drops over multiple frames. Easy right? 😅

    const ticker = new PIXI.Ticker();
    
    // The number of frames to use for smoothing
    const smoothingFrames = 10;
    
    // The smoothed frame duration
    let smoothedFrameDuration = 0;
    
    ticker.add((deltaTime) => {
      // Add the current frame duration to the smoothing array
      smoothedFrameDuration = (smoothedFrameDuration * (smoothingFrames - 1) + deltaTime) / smoothingFrames;
    
      // Update the game logic here
      // Use the smoothed frame duration instead of the raw deltaTime value
    });
    
    ticker.start();