Phaser #2: Linear Interpolation
This tutorial implements interpolation by hand to teach the concept. For the built-in 0.18 API that smooths remote entities for you, see Client Prediction → Remote entities.
This guide will show you how you can build a multiplayer experience with Colyseus Multiplayer Framework and Phaser.
In Part 2, we will explore:
- Update the player’s positions at every tick
- Use linear interpolation to smooth player’s movement
Materials
Why does Part 1 have choppy movement?
On Part 1 of this tutorial, we used callbacks.onChange() to update the player’s visual representation the instant state updates arrive from the server:
// listening for server updates
callbacks.onChange(player, () => {
// update local position immediately
entity.x = player.x;
entity.y = player.y;
});The result is “choppy”: the server sends updates less often than the frontend renders frames.
Colyseus sends state updates to the client at every 50ms (20fps) by default, whereas the frontend re-renders at every 16.6ms (60fps).
One simple yet effective way to smooth out the movement is to progressively move the player towards the latest position received at every render frame.
Applying Linear Interpolation
Cache remote position
The linear interpolation is going to be applied at every render frame.
To allow that, first we need to cache the latest player position received from the server:
// listening for server updates
callbacks.onChange(player, () => {
//
// do not update local position immediately
// we're going to LERP them during the render loop.
//
entity.setData('serverX', player.x);
entity.setData('serverY', player.y);
});Interpolate positions at every frame
Now, we are going to iterate over every player entity during our update loop. We use Phaser.Math.Linear() to move each entity slowly from its current position towards the last cached position:
update(time: number, delta: number): void {
// (...)
for (let sessionId in this.playerEntities) {
// interpolate all player entities
const entity = this.playerEntities[sessionId];
const { serverX, serverY } = entity.data.values;
entity.x = Phaser.Math.Linear(entity.x, serverX, 0.2);
entity.y = Phaser.Math.Linear(entity.y, serverY, 0.2);
}
}The third argument of Phaser.Math.Linear is the percentage value. You may want to adjust it for your own needs. It accepts from 0 to 1. The higher it is, the faster the interpolation is going to happen.