5
\$\begingroup\$

I'm developing an online browser MMO 2d war game.

And I'm having some issues with the interpolation. The server sends updated positions to the clients every 90 ms.

I've managed to make it smooth, and it works very well on low latency, but if the latency is high, everything skates around like its on ice (OVERLY SMOOTH).

I would like to achieve normal movement (visually), and I don't care about input-lag, as 3-400 ms input lag doesn't matter for this game.

This is how I'm doing it now (simplified for readability):

The "Server" object is the one processing the updates send by the server, and "animate()" is the gameloop.

    var Server = {
        msAhead: 0,
        Time: Date.now(),
        SetData: function (data) {

            //Set date.now() to calculate ms since last update
            var now = Date.now();

            //calculates ms since last update
            var ms = now - this.Time

            //sets new time (for calculating ms next update)
            this.Time = now;

            //Keeps track of how many milliseconds the server is ahead (position vice)
            this.msAhead += ms;

            //Attatch a reference with the players new position to each player
            $.each(data["players"], function (id, data) {
                Players[id].target = data;
            });
        }
    }


    var Time;
    function animate() {
        //Sets time if first call
        if (!Time) Time = Date.now();

        //Sets new time and calculate ms between frames, sets deltatime
        var now = Date.now();
        var ms = now - Time;
        var delta = ms * 0.001;

        //reset info for next frame
        Time = now;

        //Calculate and normalize lerp
        var lerp = ms / Server.msAhead;

        //update the "msAhead" so we know that we are closer to our target position
        Server.msAhead -= ms;

        //normalize lerp (it can be off on the first frames since time isn't set yet, or is heavy spikes occur)
        if (lerp > 1) lerp = 1;
        if (lerp < 0) lerp = 0;

        //Update player positions lerping towards the target position set by the server object
        $.each(Players, function (id, player) {
           player.position = Lerp(player.position, player.target.position, lerp)
        });

        //Request next animationframe
        requestAnimationFrame(animate);
    }

EDIT: I can simulate my goal on high latency by doing: var now = Date.now() + 300; in the animation loop.

But that will crash (stop updating) low latency players, since the client will think its ahead of the server, and lerp will become negative (zero).

\$\endgroup\$
  • \$\begingroup\$ It sounds like the thing you're looking for is movement prediction \$\endgroup\$ – Daniel Holst Jun 23 '15 at 18:22
2
\$\begingroup\$

Actually I solved the problem.

The solution was to manipulate the "ms" variable in the Animate() function before calculating the lerp.

I know the server sends updates every 90 ms, so if i want to add a fake delay of 100ms i need to make sure that the target "msAhead" should be 190-200.

So i did like this.

if (Server.msAhead > 200)
  ms += 1;
else if (Server.msAhead < 190)
  ms -= 1;

lerp = ms / Server.msAhead;

This looks like a hacky solution, but it actually works very well, It smoothly guides the client to be rendered at the exact delay i want. If the server is more then 100 ms ahead, it will cause the client to interpolate further to catch up.

And it looks the same on high and low latency, the difference is that people with high latency will see it all delayed. (which is not a problem in my case).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.