Hello,
I'm currently in the process of working on getting my connected players to appear smoothly lerping from the other client's perspective.
This is how my implementation works currently:
- Every .125 seconds if a particular player has moved in anyway I send updated Transform information to the server, which relays that information back to all other connected clients.
- Once that Update message is received from the server about the particular client's updated Transform information, I save it's current position, whatever it is as the previous position in my NetObjectTransformInterpolator
- These positions are stored in an std::queue<TransformInfo>, where TransformInfo contains the TimeStamp & Transform
- My TimeStamp that I append on is : double timeStamp = NetObjectTransformInterpolator::GetElapsedTime() + .175 + NetObjectTransformInterpolator::GetFrameRatio()
- My TimeStamp that I'm utilizing is just an elapsedTime hard-coded within my NetObjectTransformInterpolator that updates every frame with the current deltaTime in the LogicUpdate
- From here the NetObjectTransformInterpolator will choose the TransformInfo at the top of the std::queue and move from the particular Object's previous position to the moveTarget position, Lerping based off the time ratio between
My current implementation appears to work mostly, but there is something off about it. It's smooth mostly, but there appears to be a noticable jitter or something that I'm having trouble resolving once the player begins moving & ends moving. As well as when the player is moving at a bit of a faster speed.
Here is my code implementation:
void NetObjectTransformInterpolator::OnLogicUpdate(UpdateEvent* updateEvent)
{
NetObjectTransformInterpolator& interp = NetObjectTransformInterpolator::GetInstance();
frameRatio = 1.0 - (FrameRateController::GetFPS() / 60.0); // 60.0 is our target fps
for (auto& interpObj : interp.objectTransformInfo)
{
ObjectMoveInfo* moveInfo = interpObj.second.get();
if (moveInfo->transInfo.size())
{
if (moveInfo->needsMoveTarget)
{
moveInfo->moveTarget = &moveInfo->transInfo.front();
moveInfo->needsMoveTarget = false;
}
if (elapsedTime < moveInfo->moveTarget->timeStamp)
{
// .175 compensates for the networkUpdateTime & the frameRatio compensates for client side lag
double t = 1.0 - ((moveInfo->moveTarget->timeStamp - elapsedTime) / (.175 + frameRatio));
InterpToPosition(t, moveInfo);
InterpToRotation(t, moveInfo);
InterpToScale(t, moveInfo);
}
else // I don't know what to do here, this is supposed to extrapolate
{
InterpToPosition(1.0, moveInfo);
InterpToRotation(1.0, moveInfo);
InterpToScale(1.0, moveInfo);
}
DetermineIfReachedCurrentTarget(moveInfo);
}
}
elapsedTime += updateEvent->dt;
}
One of the main issues is I'm unsure how to extrapolate my positions if my elapsedTime has surpassed the moveInfo->moveTarget->timeStamp. Currently I just interpolate to set it straight to it's Transform, hence the 1.0 value. DetermineIfReachedCurrentTarget function is also responsible for popping the std::queue & setting the next moveInfo->moveTarget. I am also unsure about my frameRatio calculation necessity. I noticed I needed to do this once I started developing on my laptop which runs this application with a lower frame rate then my desktop.
I have been looking at some other implementations that utilize the NetworkTime from the server. I'd definitely prefer to do that, but I'm not sure if I have the correct NetworkTime calculation. Currently, every 1 second, when I receive my PING reply from my server, I also have attached to that the current SystemTime in milliseconds that I piece off and store on each client's side.
Thank you for any insight helping me to figure out this problem. My main goal here is just to make it so the client's are lerping smoothly, instead of smoothly with a noticable jitter.