I'm developing a browser multiplayer game, in which each client interpolates (linear) the frames of entities sent by the server. It doesn't look too bad at a high framerate (> 30fps), but starts to jitter at lower framerates (< 30fps) and freezes and jumps and very low framerates (< 10fps). I want to reduce the framerate, and I know it's possible (see Brutal.io which sends updates at 10fps).
This is the basic algorithm which I'm using:
- The server sends updates at a framerate (say, 10fps)
- The client renders the game at a framerate (say, 60fps)
- The client doesn't update the entities on screen to match the server's data straight up: this would look jittery
- Instead, it uses a linear interpolation function to smooth out the frames between server updates
- With the example of 10:60fps, the client would render 6 frames in between to create a smooth animation
- It does this by measuring the delta (difference) between server updates, and also with client render frames
- It then gets a multiplier by diving client delta by server delta
- Then, it calls the linear interpolation function, using the screen position, server position, and multiplier, to generate a new screen position
This snippet doesn't contain specific code, but should be good enough to demonstrate the basic overview (see comments in code for info):
var serverDelta = 1; // Setting up a variable to store the time between server updates
// Called when the server sends an update (aiming for 10fps)
function onServerUpdate(message) {
serverDelta = Date.now() - lastServerFrame;
}
// Called when the client renders (could be as high as 60fps)
var onClientRender() {
var clientDelta = Date.now() - lastUpdateFrame;
// Describes the multiplier used for the linear interpolation function
var lerpMult = clientDelta / serverDelta;
if (lerpMult > 1) { // Making sure that the screen position doesn't go beyond the server position
lerpMult = 1;
}
lastUpdateFrame = Date.now();
...
// For each entity
// ($x,$y) is position sent by server, (x,y) is current position on screen
entity.x = linearInterpolate(entity.x, entity.$x, lerpMult / 2);
entity.y = linearInterpolate(entity.y, entity.$y, lerpMult / 2);
}
function linearInterpolate(a, b, f) {
return (a * (1 - f)) + (b * f);
};
As stated above, this creates jitter and jumping in the motion. Is there something I'm doing wrong? How would I make this motion smooth?