This commit is contained in:
mischa 2024-04-26 21:40:34 +08:00
parent 68a7365232
commit a7f2a8fed3

View File

@ -70,8 +70,15 @@ struct PendingForce
public bool debugColors = false; public bool debugColors = false;
Color originalColor = Color.white; Color originalColor = Color.white;
public Color predictingColor = Color.green; public Color predictingColor = Color.green;
public Color blendingExactColor = Color.yellow;
public Color blendingGuessColor = Color.red; public Color blendingGuessColor = Color.red;
public Color blendingGuessAvgColor = Color.cyan;
// FOLLOWING.startPosition guessing.
// always average the last 3 positions to smooth out slightl jitter from guessing.
// exponential moving average for each float.
ExponentialMovingAverage guessXAvg = new ExponentialMovingAverage(3);
ExponentialMovingAverage guessYAvg = new ExponentialMovingAverage(3);
ExponentialMovingAverage guessZAvg = new ExponentialMovingAverage(3);
protected override void Awake() protected override void Awake()
{ {
@ -235,6 +242,9 @@ protected void BeginBlending()
// reset old state // reset old state
followingStartPositionEstimate = null; followingStartPositionEstimate = null;
followingStartRotationEstimate = null; followingStartRotationEstimate = null;
guessXAvg.Reset();
guessYAvg.Reset();
guessZAvg.Reset();
// remember exactly where blending started. // remember exactly where blending started.
predictionEndPosition = predictedRigidbody.position; predictionEndPosition = predictedRigidbody.position;
@ -308,6 +318,8 @@ protected override void ApplySnapshot(NTSnapshot interpolated)
Vector3? followingStartPositionEstimate; Vector3? followingStartPositionEstimate;
Quaternion? followingStartRotationEstimate; Quaternion? followingStartRotationEstimate;
Vector3 followingStartPositionEstimateAvg =>
new Vector3((float)guessXAvg.Value, (float)guessYAvg.Value, (float)guessZAvg.Value);
// Prediction uses a Rigidbody, which needs to be moved in FixedUpdate() even while kinematic. // Prediction uses a Rigidbody, which needs to be moved in FixedUpdate() even while kinematic.
double lastReceivedRemoteTime = 0; double lastReceivedRemoteTime = 0;
@ -343,6 +355,12 @@ void UpdateClient()
return; return;
} }
// debug colors
if (debugColors)
{
rend.material.color = blendingGuessColor;
}
// first principles: // first principles:
// //
// BLENDING needs to interpolate between PREDICTING & FOLLOWING. // BLENDING needs to interpolate between PREDICTING & FOLLOWING.
@ -360,6 +378,11 @@ void UpdateClient()
// do we have an estimate yet? // do we have an estimate yet?
if (!followingStartPositionEstimate.HasValue) return; if (!followingStartPositionEstimate.HasValue) return;
// use the moving average of the last 3 FOLLOWING.startPosition guesses
// TODO rotation avg?
Vector3 targetPosition = followingStartPositionEstimateAvg;
Quaternion targetRotation = followingStartRotationEstimate.Value;
// now we have the exact FOLLOW.startPosition, or a best guess. // now we have the exact FOLLOW.startPosition, or a best guess.
// interpolate from where we started to where we are going. // interpolate from where we started to where we are going.
// we started at predictionEndPosition @ blendingStartTime. // we started at predictionEndPosition @ blendingStartTime.
@ -369,11 +392,11 @@ void UpdateClient()
float blendFactor = totalBlendTime > 0 ? Mathf.Clamp01(elapsedBlendTime / totalBlendTime) : 0; // avoids divide by zero float blendFactor = totalBlendTime > 0 ? Mathf.Clamp01(elapsedBlendTime / totalBlendTime) : 0; // avoids divide by zero
// interpolate // interpolate
Vector3 targetPosition = Vector3.Lerp(followingStartPositionEstimate.Value, predictionEndPosition, blendFactor); Vector3 position = Vector3.Lerp(targetPosition, predictionEndPosition, blendFactor);
Quaternion targetRotation = Quaternion.Slerp(followingStartRotationEstimate.Value, predictionEndRotation, blendFactor); Quaternion rotation = Quaternion.Slerp(targetRotation, predictionEndRotation, blendFactor);
// set position and rotation // set position and rotation
tf.SetPositionAndRotation(targetPosition, targetRotation); tf.SetPositionAndRotation(position, rotation);
} }
// FOLLOWING sets Transform, which happens in Update(). // FOLLOWING sets Transform, which happens in Update().
else if (state == ForecastState.FOLLOWING) else if (state == ForecastState.FOLLOWING)
@ -439,6 +462,9 @@ private void OnDrawGizmos()
{ {
Gizmos.color = blendingGuessColor; Gizmos.color = blendingGuessColor;
Gizmos.DrawWireCube(followingStartPositionEstimate.Value, bounds.size); Gizmos.DrawWireCube(followingStartPositionEstimate.Value, bounds.size);
Gizmos.color = blendingGuessAvgColor;
Gizmos.DrawWireCube(followingStartPositionEstimateAvg, bounds.size);
} }
} }
@ -482,12 +508,6 @@ bool GuessForecastingStartPosition(out Vector3 position, out Quaternion rotation
position = interpolated.position; position = interpolated.position;
rotation = interpolated.rotation; rotation = interpolated.rotation;
// debug colors
if (debugColors)
{
rend.material.color = blendingExactColor;
}
return true; return true;
} }
// if not, then we need to guess. // if not, then we need to guess.
@ -515,12 +535,6 @@ bool GuessForecastingStartPosition(out Vector3 position, out Quaternion rotation
position = latest.position + velocity * timeToBlendingEnd; position = latest.position + velocity * timeToBlendingEnd;
rotation = latest.rotation * Quaternion.Slerp(Quaternion.identity, rotationDelta, timeToBlendingEnd / timeDelta); rotation = latest.rotation * Quaternion.Slerp(Quaternion.identity, rotationDelta, timeToBlendingEnd / timeDelta);
// debug colors
if (debugColors)
{
rend.material.color = blendingGuessColor;
}
return true; return true;
} }
// this shouldn't really happen. if timedelta is zero: do nothing. // this shouldn't really happen. if timedelta is zero: do nothing.
@ -561,6 +575,12 @@ protected override void OnServerToClientSync(Vector3? position, Quaternion? rota
{ {
followingStartPositionEstimate = followStartPosition; followingStartPositionEstimate = followStartPosition;
followingStartRotationEstimate = followStartRotation; followingStartRotationEstimate = followStartRotation;
// average the last 3 guesses to smooth out guess related jitter.
// otherwise it's a bit noticable since guesses jump back & forth, and so would the interpolation.
guessXAvg.Add(followStartPosition.x);
guessYAvg.Add(followStartPosition.y);
guessZAvg.Add(followStartPosition.z);
} }
else else
{ {