ijonatron

Entity interpolation with Pixi.js

Recommended Posts

Hey everyone! Just joined this forum hoping someone could help me out here...

I'm working on a test project to try out some of these concepts. I'm currently working on entity interpolation and I think I'm pretty close to it working, but the moving objects still seem jittery. On the back end I'm sending snapshots of player positions every 50ms...

// send snapshots
setInterval(() => {
  const snapshot = { timestamp: Date.now(), players };

  ws.clients.forEach(client => {
    client.send(pack('snapshot', snapshot));
  });
}, 50);

These snapshots are being received on the client like so...

const sync = snapshot => {
  // keep the last 2 snapshots
  if (snapshots.length === 2) snapshots.shift();
  snapshots.push(snapshot);

  snapshot.players.forEach(player => {
    let manager = playerManagers.find(mngr => mngr.id === player.id);
    ...
    manager.updateRemote(player);
  });

  t = 0;
};

The manager here is a class instance associated with each player that controls the sprite's position, rotation, etc. Here is that updateRemote method...

updateRemote = player => {
  this.local = this.remote ? this.remote : player;
  this.remote = player;
}

What this does is set the manager's local and remote properties. These are the states that the sprite should interpolate between.

Then here is the Pixi.js ticker...

let t = 0;
ticker.add(() => {
  t += ticker.deltaTime;

  let lag = 50;
  if (snapshots.length >= 2) lag = snapshots[1].timestamp - snapshots[0].timestamp;

  playerManagers.forEach(manager => manager.interpolate(t / lag));
});

Here I am increasing t by the ticker's deltaTime and dividing that by the time between the two snapshots (or the intended time if there aren't two snapshots available). Remember above that t is reset each time a snapshot is received. This means when a snapshot was just received, t will be 0 and start counting up. When another snapshot is received, t will be at 50 (or whatever the real lag is) then be set back to 0 and repeat. In other words, t / lag will be between 0 and 1 for the lerp function...

Here is that interpolate method...

interpolate = delta => {
  this.sprite.position.set(
    lerp(this.local.pos.x, this.remote.pos.x, delta),
    lerp(this.local.pos.y, this.remote.pos.y, delta),
  );

  this.sprite.rotation = lerp(this.local.direction, this.remote.direction, delta);
}

And the lerp function is the second formula here.

 

AFAIK this should give me smooth movement between the past snapshot (local) and the new one (remote), but movement still seems choppy and jittery. I'm fairly new to interpolation, prediction, and other game networking concepts so hopefully someone can help me out here. Please let me know if any more information is needed.

Share this post


Link to post
Share on other sites

(-‸ლ)

Just realized I was using the wrong ticker value...

Instead of adding ticker.deltaTime to t I should have been adding ticker.elapsedMS. Still very new to Pixi 😬 Maybe someone else will have the same problem and find this...

Share this post


Link to post
Share on other sites

Hey man, I didn't familiar with Pixi.js a lot, but I did my own implementation of lag interpolation in my game.
Maybe it will help you some how.

const MAX_FPS_MS = 1000 / 60


// some class implementation below.

class Renderer {

_fpsThresholdInMs = MAX_FPS_MS; // 60 fps in ms ~16.66666ms
_accumulator = 0; // Accumulates delta times.

render() {
   let dt = this._timer.checkpoint(); // Will get delta between current and prev frames.
   this._accumulator += dt; // Add delta time to accumulator.
   dt = Math.max(dt, this._fpsThresholdInMs); // Set top threshold to 60 FPS.

    if (this._accumulator >= this._fpsThresholdInMs) {
      // RENDER FRAME HERE.

      this._accumulator -= this._fpsThresholdInMs;
       
      // Usually we have delta time equal to 16.6666ms
      // In a case if more, that would be lag or frame degradation.
      // We need to scale speed by lag to predict transformation.
      const dtInterpolationFactor = dt / MAX_FPS_MS;

     // Multiply interpolation factor for any of your game objects transformations.
      scene.update(dtInterpolationFactor);
    }

   // SKIP FRAME.
}
}

I apologise for content quality, I just copied some lines from my project.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.