Prediction: call ApplyState directly; move ApplyCorrection into Compare state. prepares for more improvements later.

This commit is contained in:
mischa 2024-01-10 15:31:54 +01:00
parent 9aadcbf442
commit e06fcd5535

View File

@ -273,6 +273,19 @@ void RecordState()
void ApplyState(Vector3 position, Quaternion rotation, Vector3 velocity) void ApplyState(Vector3 position, Quaternion rotation, Vector3 velocity)
{ {
// fix rigidbodies seemingly dancing in place instead of coming to rest.
// hard snap to the position below a threshold velocity.
// this is fine because the visual object still smoothly interpolates to it.
if (rb.velocity.magnitude <= snapThreshold)
{
Debug.Log($"Prediction: snapped {name} into place because velocity {rb.velocity.magnitude:F3} <= {snapThreshold:F3}");
stateHistory.Clear();
rb.position = position;
rb.rotation = rotation;
rb.velocity = Vector3.zero;
return;
}
// Rigidbody .position teleports, while .MovePosition interpolates // Rigidbody .position teleports, while .MovePosition interpolates
// TODO is this a good idea? what about next capture while it's interpolating? // TODO is this a good idea? what about next capture while it's interpolating?
if (correctionMode == CorrectionMode.Move) if (correctionMode == CorrectionMode.Move)
@ -286,52 +299,10 @@ void ApplyState(Vector3 position, Quaternion rotation, Vector3 velocity)
rb.rotation = rotation; rb.rotation = rotation;
} }
// there's only one way to set velocity
rb.velocity = velocity; rb.velocity = velocity;
} }
void ApplyCorrection(RigidbodyState corrected, RigidbodyState before, RigidbodyState after)
{
// TODO merge this with CompareState iteration!
// first, remember the delta between last recorded state and current live state.
// before we potentially correct 'last' in history.
// TODO we always record the current state in CompareState now.
// applying live delta may not be necessary anymore.
// this should always be '0' now.
// RigidbodyState newest = stateHistory.Values[stateHistory.Count - 1];
// Vector3 livePositionDelta = rb.position - newest.position;
// Vector3 liveVelocityDelta = rb.velocity - newest.velocity;
// TODO rotation delta?
// fix rigidbodies seemingly dancing in place instead of coming to rest.
// hard snap to the position below a threshold velocity.
// this is fine because the visual object still smoothly interpolates to it.
if (rb.velocity.magnitude <= snapThreshold)
{
Debug.Log($"Prediction: snapped {name} into place because velocity {rb.velocity.magnitude:F3} <= {snapThreshold:F3}");
stateHistory.Clear();
rb.position = corrected.position;
rb.rotation = corrected.rotation;
rb.velocity = Vector3.zero;
return;
}
// show the received correction position + velocity for debugging.
// helps to compare with the interpolated/applied correction locally.
// TODO don't hardcode length?
Debug.DrawLine(corrected.position, corrected.position + corrected.velocity * 0.1f, Color.white, lineTime);
// insert the corrected state and correct all reapply the deltas after it.
RigidbodyState recomputed = Prediction.CorrectHistory(stateHistory, stateHistoryLimit, corrected, before, after, out int correctedAmount);
// log, draw & apply the final position.
// always do this here, not when iterating above, in case we aren't iterating.
// for example, on same machine with near zero latency.
// Debug.Log($"Correcting {name}: {correctedAmount} / {stateHistory.Count} states to final position from: {rb.position} to: {last.position}");
Debug.DrawLine(rb.position, recomputed.position, Color.green, lineTime);
ApplyState(recomputed.position, recomputed.rotation, recomputed.velocity);
}
// compare client state with server state at timestamp. // compare client state with server state at timestamp.
// apply correction if necessary. // apply correction if necessary.
void CompareState(double timestamp, RigidbodyState state) void CompareState(double timestamp, RigidbodyState state)
@ -382,7 +353,7 @@ void CompareState(double timestamp, RigidbodyState state)
if (state.timestamp < oldest.timestamp) if (state.timestamp < oldest.timestamp)
{ {
Debug.LogWarning($"Hard correcting client because the client is too far behind the server. History of size={stateHistory.Count} @ t={timestamp:F3} oldest={oldest.timestamp:F3} newest={newest.timestamp:F3}. This would cause the client to be out of sync as long as it's behind."); Debug.LogWarning($"Hard correcting client because the client is too far behind the server. History of size={stateHistory.Count} @ t={timestamp:F3} oldest={oldest.timestamp:F3} newest={newest.timestamp:F3}. This would cause the client to be out of sync as long as it's behind.");
ApplyCorrection(state, state, state); ApplyState(state.position, state.rotation, state.velocity);
return; return;
} }
@ -403,7 +374,7 @@ void CompareState(double timestamp, RigidbodyState state)
{ {
double ahead = state.timestamp - newest.timestamp; double ahead = state.timestamp - newest.timestamp;
Debug.Log($"Hard correction because the client is ahead of the server by {(ahead*1000):F1}ms. History of size={stateHistory.Count} @ t={timestamp:F3} oldest={oldest.timestamp:F3} newest={newest.timestamp:F3}. This can happen when latency is near zero, and is fine unless it shows jitter."); Debug.Log($"Hard correction because the client is ahead of the server by {(ahead*1000):F1}ms. History of size={stateHistory.Count} @ t={timestamp:F3} oldest={oldest.timestamp:F3} newest={newest.timestamp:F3}. This can happen when latency is near zero, and is fine unless it shows jitter.");
ApplyCorrection(state, state, state); ApplyState(state.position, state.rotation, state.velocity);
} }
return; return;
} }
@ -414,7 +385,7 @@ void CompareState(double timestamp, RigidbodyState state)
// something went very wrong. sampling should've worked. // something went very wrong. sampling should've worked.
// hard correct to recover the error. // hard correct to recover the error.
Debug.LogError($"Failed to sample history of size={stateHistory.Count} @ t={timestamp:F3} oldest={oldest.timestamp:F3} newest={newest.timestamp:F3}. This should never happen because the timestamp is within history."); Debug.LogError($"Failed to sample history of size={stateHistory.Count} @ t={timestamp:F3} oldest={oldest.timestamp:F3} newest={newest.timestamp:F3}. This should never happen because the timestamp is within history.");
ApplyCorrection(state, state, state); ApplyState(state.position, state.rotation, state.velocity);
return; return;
} }
@ -430,7 +401,20 @@ void CompareState(double timestamp, RigidbodyState state)
if (difference >= correctionThreshold) if (difference >= correctionThreshold)
{ {
// Debug.Log($"CORRECTION NEEDED FOR {name} @ {timestamp:F3}: client={interpolated.position} server={state.position} difference={difference:F3}"); // Debug.Log($"CORRECTION NEEDED FOR {name} @ {timestamp:F3}: client={interpolated.position} server={state.position} difference={difference:F3}");
ApplyCorrection(state, before, after);
// show the received correction position + velocity for debugging.
// helps to compare with the interpolated/applied correction locally.
Debug.DrawLine(state.position, state.position + state.velocity * 0.1f, Color.white, lineTime);
// insert the corrected state and correct all reapply the deltas after it.
RigidbodyState recomputed = Prediction.CorrectHistory(stateHistory, stateHistoryLimit, state, before, after, out int correctedAmount);
// log, draw & apply the final position.
// always do this here, not when iterating above, in case we aren't iterating.
// for example, on same machine with near zero latency.
// Debug.Log($"Correcting {name}: {correctedAmount} / {stateHistory.Count} states to final position from: {rb.position} to: {last.position}");
Debug.DrawLine(rb.position, recomputed.position, Color.green, lineTime);
ApplyState(recomputed.position, recomputed.rotation, recomputed.velocity);
} }
} }