Jump to content
  • Advertisement
gamingstill

How to reduce Interpolation jitter

Recommended Posts

// 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. 

 

Edited by gamingstill

Share this post


Link to post
Share on other sites
Advertisement

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

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!