🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

How to reduce Interpolation jitter

Started by
0 comments, last by gamingstill 6 years, 2 months ago

// get current local relative time
this.getCurrTimeC = function () {
        return (performance.now() - this.appStart);
    }


      // get the synchronized time 
 //   Between server and client
// _coffset is the delta time between client and server
this.getCurrTime = function () {
        var curr = performance.now() + this._coffset;
        
        this.lastCurrTime = curr;
        return (curr - this.appStart);
    }



this.processLatency = function (cp2, clTime1, servTime1, servTime2, lastSize,px,py) {
// px py are position of player on the server
       // this fn calculates the time offset between se4ver and the client
        // cp2 : current time on client
        //cltime: client time of the last input sent to server
        // servtime1 : server time when last input recieved from client
        //servtime2 : server time when the last packet sent
        // all these times are delta times like cp2 is the performance.now- appstarttime
        
        this.lastServTime = this.servTime;
        this.servTime = servTime2;
        this.packDt = this.servTime - this.lastServTime;
        var rtt = this.roundTripTime = cp2 - clTime1;
        var now = this.getCurrTime();
        var spp = servTime2 - servTime1;
        /// more than 100s with no input
        this.latency = (rtt - spp) / 2;
        this.lat_coll.push(this.latency);
        this.alatency = Math.floor(this.lat_coll.reduce(function (a, b) {
            return a + b;
        }) / this.lat_coll.length);
        if (this.packRec % 19 === 0) {
            this._coffset = ((servTime2 + this.alatency - cp2) + (servTime1 - this.alatency - clTime1)) * 0.5;
        }
        this.packRec++;
        this.ctimeArr.length > 10 && this.ctimeArr.splice(0, 1);
        this.ctimeArr.push(this._coffset);
        this._coffset_avg = Math.floor(this.ctimeArr.reduce(function (a, b) {
            return a + b;
        }) / this.ctimeArr.length);
        this.lat_coll.length > Conf.LAT_SIZE && this.lat_coll.splice(0, 1);
        this.lastPackFromServ = this.getCurrTime();
        this.lastPackSize = lastSize;
        this.player.pushSnapshots(servTime2,now,px,py);
       
    };



Player.prototype.pushSnapshots = function (sTime, now, px, py) {
          // ip is the interpolator
          // stime is the time when server sent the packet and now is current sync time.
    this.ip.addSample(sTime, now, px, py);
};


// currentTime is the sync time
Player.prototype.updatePlayer = function (currentTime) {

    var ret = this.ip.getPosition(currentTime, this.id);

    if (ret) {
        this.position.x = ret[0];
        this.position.y = ret[1];
    }
};


// interpolator 


var PI = Math.PI;
var TWO_PI = Math.PI * 2;
var DEF_UP = 100;
var MAX_STICK_TIME = 350; // 
var Vec2D = function (x, y) {
    this.x = x || 0;
    this.y = y || 0;
}
var Interpolator = function (id) {
    this.id = id;
    this.SnapPosition = new Vec2D(0, 0);
    this.SnapVelocity = new Vec2D(0, 0);
    this.AimPos = new Vec2D(0, 0);
    this.LastPackPos = new Vec2D(0, 0);
    this.SnapTime = 0;
    this.AimTime = 0;
    this.Latency = 0;
    this.UpdateTime = DEF_UP;
    this.LastPacketTime = 0;
};

module.exports = Interpolator;

Interpolator.prototype.reset = function (pt, ct, pos) {
    this.LastPacketTime = pt;
    this.LastPackPos.x = this.SnapPosition.x = pos.x;
    this.LastPackPos.y = this.SnapPosition.y = pos.y;
    this.SnapTime = ct;
    this.UpdateTime = DEF_UP;
    this.Latency = this.UpdateTime;
    this.AimTime = ct + this.UpdateTime;

    this.AimPos.x = this.SnapPosition.x + this.SnapVelocity.x * this.UpdateTime;
    this.AimPos.y = this.SnapPosition.y + this.SnapVelocity.y * this.UpdateTime;
}

Interpolator.prototype.addSample = function (packetTime, currTime, px,py) {
    var dt = 0,
        snapRead = [],
        packDel = 1.0 / (packetTime - this.LastPacketTime);
        var MA = Math.abs;
    if (MA(packetTime - this.LastPacketTime) < 0.0001 || !this.Smooth(packetTime, currTime)) {
        return null; // neglect this sample
    }
    var _a = MA(px - this.LastPackPos.x);
    var _b = MA(py - this.LastPackPos.y);
    if(_a>300 || _b>300){
        this.SnapPosition.x = px;
        this.SnapPosition.y = py;
    }
    this.LastPackPos.x = px;
    this.LastPackPos.y = py;
    this.LastPacketTime = packetTime;
    snapRead = this.getPosition(currTime);
    this.SnapPosition.x = snapRead[0];
    this.SnapPosition.y = snapRead[1];
    this.AimTime = currTime + this.UpdateTime;
    this.SnapTime = currTime;
    this.AimPos.x = px;
    this.AimPos.y = py;
    if ((MA(this.AimTime - this.SnapTime) >= 0.0001)) {
        dt = 1 / (this.AimTime - this.SnapTime);
        this.SnapVelocity.x = (this.AimPos.x - this.SnapPosition.x) * dt;
        this.SnapVelocity.y = (this.AimPos.y - this.SnapPosition.y) * dt;
     }else{
        this.SnapVelocity.x =0;
        this.SnapVelocity.y = 0;
    }
 
};

Interpolator.prototype.Smooth = function (packtime, currtime) {
    if (packtime <= this.LastPacketTime) {
        return false;
    }
    var lat = currtime - packtime;
    var tick = packtime - this.LastPacketTime;
    if(tick>MAX_STICK_TIME){
        this.LastPacketTime = packtime - 30;
        tick = 50;
    }
    lat < 0 && (lat = 0);
    this.Latency = (lat > this.Latency) ? ((this.Latency + lat) * 0.5) : ((this.Latency * 7 + lat) * 0.125);
    this.UpdateTime = (tick > this.UpdateTime) ? ((this.UpdateTime + tick) * 0.5) : ((this.UpdateTime * 7 + tick) * 0.125);
    return true;
};

Interpolator.prototype.getPosition = function (forTime,id) {
    var maxRange = this.AimTime + this.UpdateTime;
    forTime < this.SnapTime && (forTime = this.SnapTime);
    forTime > maxRange && (forTime = maxRange);
    var max = forTime - this.SnapTime;
    var getPos = [this.SnapPosition.x + this.SnapVelocity.x * max, this.SnapPosition.y + this.SnapVelocity.y * max];
    return getPos;
};

The code above is based on code from @hplus0603

The code above works OK but there is some jitter from time to time. 

Any help will be greatly appreciated. 

Thanks. 

 

This topic is closed to new replies.

Advertisement