In a fast-paced multiplayer game I'm working on, there is an issue with the interpolation algorithm. This interpolation is for a moving object not controlled by the local computer.
The server sends packets with the object's position at a fixed rate. (red markers) The interpolation algorithm is attempting (and failing) to create a smooth path between the red markers.
The results of this interpolation are shown by green and black markers. Green is the local position at the exact time a packet is received, and black is the local position at every frame.
Green: Local position when a packet is received (edited for clarity)
Red: Position received from packet (goal)
Blue: Line from local position to goal when packet is received
Black: Local position every frame
On a network with a realistic amount of lag and packet loss, this is the result:
The local position (green&black lines) seems to oscillate around the goals (red lines) instead of moving between them smoothly.
This is how it looks on a perfect network with the object moving right to left. (0 ping, 0 packet loss, etc.)
The code works like this:
- Every x ms, receive a packet with a Vector3 position
- Set the
goal
to that position - Set the
positionAtLastPacket
to the current local position - Every frame, lerp from
positionAtLastPacket
togoal
, attempting to reach the goal approximately when the next packet should arrive. - If the next packet takes longer than expected, interpolation continues past the goal and becomes extrapolation.
I believe what is happening is the extrapolation overshoots a bit to far. Over several packets, this issue compounds, causing the oscillation.
// local transform position when the last packet arrived. Will lerp from here to the goal
private Vector3 positionAtLastPacket;
// location received from last packet
private Vector3 goal;
// time since the last packet arrived
private float currentTime;
// estimated time to reach goal (also the expected time of the next packet)
private float timeToReachGoal;
private void PacketReceived(Vector3 position, float timeBetweenPackets)
{
positionAtLastPacket = transform.position;
goal = position;
timeToReachGoal = timeBetweenPackets;
currentTime = 0;
Debug.DrawRay(transform.position, Vector3.up, Color.cyan, 5); // current local position
Debug.DrawLine(transform.position, goal, Color.blue, 5); // path to goal
Debug.DrawRay(goal, Vector3.up, Color.red, 5); // received goal position
}
private void FrameUpdate()
{
currentTime += Time.deltaTime;
float delta = currentTime/timeToReachGoal;
transform.position = FreeLerp(positionAtLastPacket, goal, currentTime / timeToReachGoal);
// current local position
Debug.DrawRay(transform.position, Vector3.up * 0.5f, Color.black, 5);
}
/// <summary>
/// Lerp without being locked to 0-1
/// </summary>
Vector3 FreeLerp(Vector3 from, Vector3 to, float t)
{
return from + (to - from) * t;
}
Any idea about what's going on?