Skip to content

Commit bc2e9f2

Browse files
wip
Getting closer to what I am hoping to achieve.
1 parent 8f2981b commit bc2e9f2

File tree

3 files changed

+127
-71
lines changed

3 files changed

+127
-71
lines changed

com.unity.netcode.gameobjects/Runtime/Components/Interpolator/BufferedLinearInterpolator.cs

Lines changed: 81 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,15 @@ internal struct CurrentState
121121
public float TimeToTargetValue;
122122
public float DeltaTime;
123123
public float LerpT;
124-
124+
public bool TargetReached;
125125
public T CurrentValue;
126126
public T PreviousValue;
127+
public T PredictValue;
127128

128129
private float m_AverageDeltaTime;
129130

130131
public float AverageDeltaTime => m_AverageDeltaTime;
131-
public float FinalTimeToTarget => TimeToTargetValue - DeltaTime;
132+
public float FinalTimeToTarget => Mathf.Max(0.0f, TimeToTargetValue - DeltaTime);
132133

133134
public void AddDeltaTime(float deltaTime)
134135
{
@@ -151,20 +152,21 @@ public void ResetDelta()
151152
DeltaTime = 0.0f;
152153
}
153154

154-
public bool TargetTimeAproximatelyReached()
155+
public bool TargetTimeAproximatelyReached(float adjustForNext = 1.0f)
155156
{
156157
if (!Target.HasValue)
157158
{
158159
return false;
159160
}
160-
return m_AverageDeltaTime >= FinalTimeToTarget;
161+
return (m_AverageDeltaTime * adjustForNext) >= FinalTimeToTarget;
161162
}
162163

163164
public void Reset(T currentValue)
164165
{
165166
Target = null;
166167
CurrentValue = currentValue;
167168
PreviousValue = currentValue;
169+
TargetReached = false;
168170
// When reset, we consider ourselves to have already arrived at the target (even if no target is set)
169171
LerpT = 0.0f;
170172
EndTime = 0.0;
@@ -273,69 +275,81 @@ private void InternalReset(T targetValue, double serverTime, bool isAngularValue
273275
/// <param name="maxDeltaTime">maximum time delta which defines the maximum time duration when consuming more than one item from the buffer</param>
274276
private void TryConsumeFromBuffer(double renderTime, float minDeltaTime, float maxDeltaTime)
275277
{
276-
if (!InterpolateState.Target.HasValue || (InterpolateState.Target.Value.TimeSent <= renderTime
277-
&& (InterpolateState.TargetTimeAproximatelyReached() || IsAproximately(InterpolateState.CurrentValue, InterpolateState.Target.Value.Item))))
278+
BufferedItem? previousItem = null;
279+
var startTime = 0.0;
280+
var alreadyHasBufferItem = false;
281+
var noStateSet = !InterpolateState.Target.HasValue;
282+
var potentialItemNeedsProcessing = false;
283+
var currentTargetTimeReached = false;
284+
285+
while (m_BufferQueue.TryPeek(out BufferedItem potentialItem))
278286
{
279-
BufferedItem? previousItem = null;
280-
var startTime = 0.0;
281-
var alreadyHasBufferItem = false;
282-
while (m_BufferQueue.TryPeek(out BufferedItem potentialItem))
287+
// If we are still on the same buffered item (FIFO Queue), then exit early as there is nothing
288+
// to consume.
289+
if (previousItem.HasValue && previousItem.Value.TimeSent == potentialItem.TimeSent)
283290
{
284-
// If we are still on the same buffered item (FIFO Queue), then exit early as there is nothing
285-
// to consume.
286-
if (previousItem.HasValue && previousItem.Value.TimeSent == potentialItem.TimeSent)
291+
break;
292+
}
293+
294+
if (!noStateSet)
295+
{
296+
potentialItemNeedsProcessing = (potentialItem.TimeSent <= renderTime) && potentialItem.TimeSent >= InterpolateState.Target.Value.TimeSent;
297+
currentTargetTimeReached = InterpolateState.TargetTimeAproximatelyReached(potentialItemNeedsProcessing ? 1.5f : 1.0f);
298+
if (!potentialItemNeedsProcessing && !InterpolateState.TargetReached)
287299
{
288-
break;
300+
InterpolateState.TargetReached = IsAproximately(InterpolateState.CurrentValue, InterpolateState.Target.Value.Item);
289301
}
302+
}
290303

291-
// If we haven't set a target or the potential item's time sent is less that the current target's time sent
292-
// then pull the BufferedItem from the queue. The second portion of this accounts for scenarios where there
293-
// was bad latency and the buffer has more than one item in the queue that is less than the renderTime. Under
294-
// this scenario, we just want to continue pulling items from the queue until the last item pulled from the
295-
// queue is greater than the redner time or greater than the currently targeted item.
296-
if (!InterpolateState.Target.HasValue ||
297-
((potentialItem.TimeSent <= renderTime) && InterpolateState.Target.Value.TimeSent <= potentialItem.TimeSent))
304+
// If we haven't set a target or the potential item's time sent is less that the current target's time sent
305+
// then pull the BufferedItem from the queue. The second portion of this accounts for scenarios where there
306+
// was bad latency and the buffer has more than one item in the queue that is less than the renderTime. Under
307+
// this scenario, we just want to continue pulling items from the queue until the last item pulled from the
308+
// queue is greater than the redner time or greater than the currently targeted item.
309+
if (noStateSet || ((currentTargetTimeReached || InterpolateState.TargetReached) && potentialItemNeedsProcessing))
310+
{
311+
if (m_BufferQueue.TryDequeue(out BufferedItem target))
298312
{
299-
if (m_BufferQueue.TryDequeue(out BufferedItem target))
313+
if (!InterpolateState.Target.HasValue)
300314
{
301-
if (!InterpolateState.Target.HasValue)
315+
InterpolateState.Target = target;
316+
317+
alreadyHasBufferItem = true;
318+
InterpolateState.PredictValue = InterpolateState.CurrentValue;
319+
InterpolateState.PreviousValue = InterpolateState.CurrentValue;
320+
InterpolateState.TimeToTargetValue = minDeltaTime;
321+
startTime = InterpolateState.Target.Value.TimeSent;
322+
InterpolateState.TargetReached = false;
323+
}
324+
else
325+
{
326+
if (!alreadyHasBufferItem)
302327
{
303-
InterpolateState.Target = target;
304-
305328
alreadyHasBufferItem = true;
306-
InterpolateState.PreviousValue = InterpolateState.CurrentValue;
307-
InterpolateState.TimeToTargetValue = minDeltaTime;
308329
startTime = InterpolateState.Target.Value.TimeSent;
330+
//InterpolateState.PreviousValue = InterpolateState.CurrentValue;
331+
//InterpolateState.PredictValue = InterpolateState.CurrentValue;
332+
InterpolateState.LerpT = 0.0f;
333+
InterpolateState.TargetReached = false;
309334
}
310-
else
311-
{
312-
if (!alreadyHasBufferItem)
313-
{
314-
alreadyHasBufferItem = true;
315-
startTime = InterpolateState.Target.Value.TimeSent;
316-
InterpolateState.PreviousValue = InterpolateState.CurrentValue;
317-
InterpolateState.LerpT = 0.0f;
318-
}
319-
// TODO: We might consider creating yet another queue to add these items to and assure that the time is accelerated
320-
// for each item as opposed to losing the resolution of the values.
321-
InterpolateState.TimeToTargetValue = Mathf.Clamp((float)(target.TimeSent - startTime), minDeltaTime, maxDeltaTime);
322-
323-
InterpolateState.Target = target;
324-
}
325-
InterpolateState.ResetDelta();
335+
// TODO: We might consider creating yet another queue to add these items to and assure that the time is accelerated
336+
// for each item as opposed to losing the resolution of the values.
337+
InterpolateState.TimeToTargetValue = Mathf.Clamp((float)(target.TimeSent - startTime), minDeltaTime, maxDeltaTime);
338+
InterpolateState.Target = target;
326339
}
340+
InterpolateState.ResetDelta();
327341
}
328-
else
329-
{
330-
break;
331-
}
342+
}
343+
else
344+
{
345+
break;
346+
}
332347

333-
if (!InterpolateState.Target.HasValue)
334-
{
335-
break;
336-
}
337-
previousItem = potentialItem;
348+
if (!InterpolateState.Target.HasValue)
349+
{
350+
break;
338351
}
352+
previousItem = potentialItem;
339353
}
340354
}
341355

@@ -355,18 +369,24 @@ internal T Update(float deltaTime, double tickLatencyAsTime, float minDeltaTime,
355369
{
356370
TryConsumeFromBuffer(tickLatencyAsTime, minDeltaTime, maxDeltaTime);
357371
// Only interpolate when there is a start and end point and we have not already reached the end value
358-
if (InterpolateState.Target.HasValue)
372+
if (InterpolateState.Target.HasValue && !InterpolateState.TargetReached)
359373
{
360374
InterpolateState.AddDeltaTime(deltaTime);
361375

362-
// Smooth dampen our current time
363-
var current = SmoothDamp(InterpolateState.CurrentValue, InterpolateState.Target.Value.Item, ref m_RateOfChange, InterpolateState.TimeToTargetValue, InterpolateState.DeltaTime);
364-
// Smooth dampen a predicted time based on our average delta time
365-
var predict = SmoothDamp(InterpolateState.CurrentValue, InterpolateState.Target.Value.Item, ref m_PredictedRateOfChange, InterpolateState.TimeToTargetValue, InterpolateState.DeltaTime + (InterpolateState.AverageDeltaTime * 2));
366-
// Lerp between the current and predicted.
367-
// Note: Since smooth dampening cannot over shoot, both current and predict will eventually become the same or will be very close to the same.
368-
// Upon stopping motion, the final resing value should be a very close aproximation of the authority side.
369-
InterpolateState.CurrentValue = Interpolate(current, predict, deltaTime);
376+
{
377+
378+
// Smooth dampen our current time
379+
InterpolateState.PreviousValue = SmoothDamp(InterpolateState.PreviousValue, InterpolateState.Target.Value.Item, ref m_RateOfChange, InterpolateState.TimeToTargetValue, InterpolateState.DeltaTime);
380+
InterpolateState.PredictValue = SmoothDamp(InterpolateState.PredictValue, InterpolateState.Target.Value.Item, ref m_PredictedRateOfChange, InterpolateState.TimeToTargetValue, Mathf.Min(InterpolateState.TimeToTargetValue, InterpolateState.DeltaTime + InterpolateState.AverageDeltaTime));
381+
// Smooth dampen a predicted time based on our minimum delta time
382+
//var predict = SmoothDamp(InterpolateState.CurrentValue, InterpolateState.Target.Value.Item, ref m_PredictedRateOfChange, InterpolateState.TimeToTargetValue, Mathf.Min(InterpolateState.TimeToTargetValue, InterpolateState.DeltaTime + InterpolateState.AverageDeltaTime));
383+
384+
// Lerp between the current and predicted.
385+
// Note: Since smooth dampening cannot over shoot, both current and predict will eventually become the same or will be very close to the same.
386+
// Upon stopping motion, the final resing value should be a very close aproximation of the authority side.
387+
InterpolateState.CurrentValue = Interpolate(InterpolateState.PreviousValue, InterpolateState.PredictValue, deltaTime);
388+
389+
}
370390
}
371391
m_NbItemsReceivedThisFrame = 0;
372392
return InterpolateState.CurrentValue;

com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3836,11 +3836,12 @@ private void UpdateInterpolation()
38363836

38373837
var cachedServerTime = m_CachedNetworkManager.ServerTime.Time;
38383838
#if COM_UNITY_MODULES_PHYSICS || COM_UNITY_MODULES_PHYSICS2D
3839-
var cachedDeltaTime = m_UseRigidbodyForMotion ? Time.fixedDeltaTime : Time.deltaTime;
3839+
//var cachedDeltaTime = m_UseRigidbodyForMotion ? Time.fixedDeltaTime : Time.deltaTime;
3840+
var cachedDeltaTime = Time.deltaTime;
38403841
#else
38413842
var cachedDeltaTime = Time.deltaTime;
38423843
#endif
3843-
var tickLatency = m_CachedNetworkManager.NetworkTimeSystem.TickLatency;
3844+
var tickLatency = m_CachedNetworkManager.NetworkTimeSystem.TickLatency + InterpolationBufferTickOffset;
38443845

38453846
// If using an owner authoritative motion model
38463847
if (!IsServerAuthoritative())
@@ -3866,8 +3867,11 @@ private void UpdateInterpolation()
38663867
// frame update. Since smooth dampening is most useful for Rigidbody motion, the physics update
38673868
// frequency is roughly 60hz (59.x?) which 2x that value as frequency is typically close to 32-33ms.
38683869
// Look within the Interpolator.Update for smooth dampening to better understand the above.
3869-
var maxDeltaTime = (1.666667f * m_CachedNetworkManager.ServerTime.FixedDeltaTime);
38703870

3871+
3872+
//var frameRateRatio = Application.targetFrameRate > 0 ? Application.targetFrameRate * cachedDeltaTime : 100 * cachedDeltaTime;
3873+
//var maxDeltaTime = (Math.Max(60, frameRateRatio * m_CachedNetworkManager.ServerTime.FixedDeltaTime));
3874+
var maxDeltaTime = tickLatency * minDeltaTime;
38713875
// Now only update the interpolators for the portions of the transform being synchronized
38723876
if (SynchronizePosition)
38733877
{
@@ -3942,6 +3946,16 @@ public virtual void OnUpdate()
39423946
}
39433947

39443948
#if COM_UNITY_MODULES_PHYSICS || COM_UNITY_MODULES_PHYSICS2D
3949+
3950+
internal void OnFixedUpdateInterpolate()
3951+
{
3952+
// Update interpolation when enabled
3953+
if (Interpolate)
3954+
{
3955+
UpdateInterpolation();
3956+
}
3957+
}
3958+
39453959
/// <summary>
39463960
/// When paired with a NetworkRigidbody and NetworkRigidbody.UseRigidBodyForMotion is enabled,
39473961
/// this will be invoked during <see cref="NetworkRigidbody.FixedUpdate"/>.
@@ -3958,10 +3972,10 @@ public virtual void OnFixedUpdate()
39583972

39593973

39603974
// Update interpolation when enabled
3961-
if (Interpolate)
3962-
{
3963-
UpdateInterpolation();
3964-
}
3975+
//if (Interpolate)
3976+
//{
3977+
// UpdateInterpolation();
3978+
//}
39653979

39663980
// Apply the current authoritative state
39673981
ApplyAuthoritativeState();
@@ -4118,12 +4132,12 @@ private void UpdateTransformState()
41184132

41194133
#region NETWORK TICK REGISTRATOIN AND HANDLING
41204134
private static Dictionary<NetworkManager, NetworkTransformTickRegistration> s_NetworkTickRegistration = new Dictionary<NetworkManager, NetworkTransformTickRegistration>();
4121-
4135+
internal static int InterpolationBufferTickOffset = 2;
41224136
internal static float GetTickLatency(NetworkManager networkManager)
41234137
{
41244138
if (networkManager.IsListening)
41254139
{
4126-
return (float)(networkManager.NetworkTimeSystem.TickLatency + networkManager.LocalTime.TickOffset);
4140+
return (float)(networkManager.NetworkTimeSystem.TickLatency + InterpolationBufferTickOffset + networkManager.LocalTime.TickOffset);
41274141
}
41284142
return 0;
41294143
}
@@ -4145,7 +4159,7 @@ internal static float GetTickLatencyInSeconds(NetworkManager networkManager)
41454159
{
41464160
if (networkManager.IsListening)
41474161
{
4148-
return (float)networkManager.LocalTime.TimeTicksAgo(networkManager.NetworkTimeSystem.TickLatency).Time;
4162+
return (float)networkManager.LocalTime.TimeTicksAgo(networkManager.NetworkTimeSystem.TickLatency + InterpolationBufferTickOffset).Time;
41494163
}
41504164
return 0f;
41514165
}

com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,29 @@ public void NetworkUpdate(NetworkUpdateStage updateStage)
361361
case NetworkUpdateStage.PreUpdate:
362362
{
363363
NetworkTimeSystem.UpdateTime();
364+
#if COM_UNITY_MODULES_PHYSICS
365+
foreach (var networkObjectEntry in NetworkTransformFixedUpdate)
366+
{
367+
// if not active or not spawned then skip
368+
if (!networkObjectEntry.Value.gameObject.activeInHierarchy || !networkObjectEntry.Value.IsSpawned)
369+
{
370+
continue;
371+
}
372+
373+
foreach (var networkTransformEntry in networkObjectEntry.Value.NetworkTransforms)
374+
{
375+
// only update if enabled
376+
if (networkTransformEntry.enabled)
377+
{
378+
networkTransformEntry.OnFixedUpdateInterpolate();
379+
}
380+
}
381+
}
382+
#endif
383+
364384
AnticipationSystem.Update();
385+
386+
365387
}
366388
break;
367389
case NetworkUpdateStage.PreLateUpdate:

0 commit comments

Comments
 (0)