Skip to main content
GameDev.net gamedev.net
🔒 Locked 🎮 Unity

Linear Interpolation

Started by DeadlyDan Aug 5, 2006 at 8:12 PM 6 replies 1.8k views
Original Post
DeadlyDan
DeadlyDan
Hey guys, i've been developing a simple 3d engine, at the current moment in time i have my client and server networking systems and i send and recieve player coordinates through packets. I am trying to implement a simple linear interpolation to smooth off "warping" look caused by lag but i am having some trouble with it. This is my linear interpolation functions:

float GetGradient ( float fStartCoord, int iStartTime, float fEndCoord, int iEndTime )
{
	if ( ( iStartTime - iEndTime ) == 0 ) return 0.0f;
	
	return ( fEndCoord - fStartCoord ) / ( iEndTime - iStartTime );
}

float Interpolate ( float fStartCoord, int iStartTime, float fEndCoord, int iEndTime, int iCurrentTime, float& fMultiplier )
{
	float fResult, fGradient, fIntercept;

	fGradient = GetGradient ( fStartCoord, iStartTime, fEndCoord, iEndTime );
	
	fMultiplier = fGradient;

	fIntercept = ( fEndCoord - fStartCoord ) - fGradient * ( iEndTime - iStartTime );

	fResult = fGradient * ( iCurrentTime - iStartTime) + fIntercept;

	return fResult;
}
In my render frame loop i have a while loop which interpolates the X Y Z coordinates based on the last recieved X Y Z coordinates and the time it took to send the packets to the recieving client. At this moment in time, this is all i require. I've looked into Cublic and Quadratic Interpolation but both seem to advance for me at the moment. In my While loop in the render frame, i check if the distance between the Interpolated XYZ coordinates equals to 0 if so it breaks the loop. This is my code for the while loop:

Vector3 vOutput;
float f;
while ( GetDistanceBetween ( vOrigin, vOutput ) != 0.0f )
{
	vOutput.fX = Interpolate ( vOriginOld.fX, iStartTime, vOrigin.fX, iEndTime, GetTickCount (), f );
	vOutput.fY = Interpolate ( vOriginOld.fY, iStartTime, vOrigin.fY, iEndTime, GetTickCount (), f );
	vOutput.fZ = Interpolate ( vOriginOld.fZ, iStartTime, vOrigin.fZ, iEndTime, GetTickCount (), f );
					
	SetOrigin ( vOutput );
}

vOriginOld = vOrigin;
The server does get relative time correctly etc. iEndTime equals to iStartTime plus how long it took to send the packet from client one to client two. The result is the whole game just freezes up and i cannot see the interpolation. Any help you could give me would be greatly appreciated. Thanks.
DeadlyDan
DeadlyDan
Sorry about the above post, i've just studied your code and it looks like it is infact, not that advance. It also seems perfect for what i want to do. The only problem is your time type. My time type is an int which i use GetTickCount to get the current time. Would it still work if i replaced Time with Int and used my time stuff.

Thanks.

|edit|

Ok i have implemented it. But, it freezes just like in my code on the first post :(

Interpolator<InterpolateVector3, int>::Settings setting;setting.calcAhead = iDifference;setting.displayBehind = iDifference;setting.slopTime = 80;Interpolator<InterpolateVector3, int> terp(setting, InterpolateVector3(vVel.fX, vVel.fY, vVel.fZ), InterpolateVector3(vPos.fX, vPos.fY, vPos.fZ), iStartTime);			InterpolateVector3 vVelIn, vPosIn;vPosIn.x = vPos.fX;vPosIn.y = vPos.fY;vPosIn.z = vPos.fZ;vVelIn.x = vVel.fX;vVelIn.y = vVel.fY;vVelIn.z = vVel.fZ;terp.update ( vPosIn, vVelIn, GetTickCount () );Vector3 vVelOut, vPosOut;while ( GetTickCount () < iStartTime + iDifference ) {	terp.read ( &vPosIn, &vVelIn, GetTickCount () );	vPosOut.fX = vPosIn.x;	vPosOut.fY = vPosIn.y;	vPosOut.fZ = vPosIn.z;	vVelOut.fX = vVelIn.x;	vVelOut.fY = vVelIn.y;	vVelOut.fZ = vVelIn.z;	Vector3 vPosRes, vVelRes;	vPosRes = vOrigin + vPosOut; 	vVelRes = vVelocity + vVelOut;	SetPos ( vPosRes );	SetVel ( vVelRes );}


[Edited by - DeadlyDan on August 7, 2006 1:51:04 AM]
hplus0603
hplus0603
Quote:
while ( GetTickCount () < iStartTime + iDifference )


This is probably the problem.

You should never need to loop over the setPos()/setOri() part. Instead, you should just call your timer once, and set your pos/ori based on that time (compared to the difference in the accumulator).

You should also compare that you never attempt to interpolate more than, say, one second out.

Try removing the entire while() loop, and replacing it by a simple call to read(). Also, you should call update() when you receive a network packet, and read() when you actually render -- supposedly, you render a lot more often than you receive packets.
enum Bool { True, False, FileNotFound };
DeadlyDan
DeadlyDan
Hey, thanks for that i've made the changes needed but there is one problem. It doesn't look like it is interpolating though :(
PlasmaAu
PlasmaAu
Quote:
Original post by hplus0603
You could try my interpolation article (newly updated).


I was checking that link out, and having some trouble implementing it.

Assuming that the update method gets called each time a packet is received for each player, and the time parameter is 'now' (when the packet is received).

What about the 'read' method?
I was calling it every loop, however the object does not move / it appears that I am doing something wrong.

Would I be going about this the correct way as I have described above?

Thanks
hplus0603
hplus0603
The documentation for update() states:

//! \param newTime The game time at which the readings were taken.


This is the time in simulation ticks that the original client sent the data. It has to be part of the data that comes in your network packet.

The documentation for read() states:

//! \param atTime The game time at which to estimate the position (typically //! later than the last received packet).


This means that "atTime" should be the time for which you want to estimate (and thus draw) the position. Typically, "atTime" is the game tick timer at the time of drawing, on the client.

When initializing the interpolator, pay attention to the values you pass in:

    //! Initialize the behavior of the interpolator. You should typically     //! set calcAhead and displayBehind to an estimate of the latency of     //! your connection, and slopTime to the maximum time without updates     //! that you are willing to go before stopping the interpolation.    struct Settings {      Time calcAhead;       //!<  How far forward to extrapolate      Time displayBehind;   //!<  How far back to interpolate      Time slopTime;        //!<  How long without updates is OK    };


calcAhead and displayBehind should typically be related to your ping time -- set them to 0.15 seconds (150 milliseconds) for an initial start, and try tuning from there.

The "slopTime" parameter is important. If you try to read() for a time that is later than the last updated time plus slopTime, it will not interpolate further out. This is to avoid entities running off to the horizon when packets get lost. A typical value for slopTime will be about 3x your packet update rate, plus calcAhead. I e, if you get packets 10 times a second, and use 150 ms calcAhead set slopTime to 450 ms (0.45 seconds).

enum Bool { True, False, FileNotFound };

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.