@@ -14,7 +14,7 @@ public abstract class BufferedLinearInterpolator<T> where T : struct
1414 // Constant absolute value for max buffer count instead of dynamic time based value. This is in case we have very low tick rates, so
1515 // that we don't have a very small buffer because of this.
1616 private const int k_BufferCountLimit = 100 ;
17- private const float k_AproximatePrecision = 0.0001f ;
17+ private const float k_AproximatePrecision = 0.00001f ;
1818 private const double k_SmallValue = 9.999999439624929E-11 ; // copied from Vector3's equal operator
1919
2020 #region Legacy notes
@@ -277,23 +277,21 @@ private void InternalReset(T targetValue, double serverTime, bool addMeasurement
277277 }
278278 }
279279
280- #region Smooth Dampening and Lerp Extrapolate Blend Interpolation Handling
280+ #region Smooth Dampening and Lerp Ahead Interpolation Handling
281281 /// <summary>
282- /// TryConsumeFromBuffer: Smooth Dampening Version
282+ /// TryConsumeFromBuffer: Smooth Dampening and Lerp Ahead Version
283283 /// </summary>
284284 /// <param name="renderTime">render time: the time in "ticks ago" relative to the current tick latency.</param>
285285 /// <param name="minDeltaTime">minimum time delta (defaults to tick frequency).</param>
286286 /// <param name="maxDeltaTime">maximum time delta which defines the maximum time duration when consuming more than one item from the buffer.</param>
287- /// <param name="extrapolateAhead ">when true, the predicted target <see cref="CurrentState.Phase2Value"/> will lerp slightly ahead of the target .</param>
288- private void TryConsumeFromBuffer ( double renderTime , double minDeltaTime , double maxDeltaTime , bool extrapolateAhead )
287+ /// <param name="lerpAhead ">when true, the predicted target <see cref="CurrentState.Phase2Value"/> will lerp towards the next target by the current delta .</param>
288+ private void TryConsumeFromBuffer ( double renderTime , double minDeltaTime , double maxDeltaTime , bool lerpAhead )
289289 {
290290 BufferedItem ? previousItem = null ;
291291 var startTime = 0.0 ;
292292 var alreadyHasBufferItem = false ;
293293 var noStateSet = ! InterpolateState . Target . HasValue ;
294294 var potentialItemNeedsProcessing = false ;
295- var currentTargetTimeReached = false ;
296- var dequeuedCount = 0 ;
297295
298296 // In the event there is nothing left in the queue (i.e. motion/change stopped), we still need to determine if the target has been reached.
299297 if ( ! noStateSet && m_BufferQueue . Count == 0 )
@@ -317,24 +315,18 @@ private void TryConsumeFromBuffer(double renderTime, double minDeltaTime, double
317315
318316 if ( ! noStateSet )
319317 {
320- potentialItemNeedsProcessing = ( potentialItem . TimeSent <= renderTime ) && potentialItem . TimeSent >= InterpolateState . Target . Value . TimeSent ;
321- currentTargetTimeReached = InterpolateState . TargetTimeAproximatelyReached ( potentialItemNeedsProcessing ) || InterpolateState . TargetReached ;
318+ potentialItemNeedsProcessing = ( potentialItem . TimeSent <= renderTime ) && potentialItem . TimeSent > InterpolateState . Target . Value . TimeSent ;
322319 if ( ! InterpolateState . TargetReached )
323320 {
324321 InterpolateState . TargetReached = IsAproximately ( InterpolateState . CurrentValue , InterpolateState . Target . Value . Item ) ;
325322 }
326323 }
327324
328- // If we haven't set a target or the potential item's time sent is less that the current target's time sent
329- // then pull the BufferedItem from the queue. The second portion of this accounts for scenarios where there
330- // was bad latency and the buffer has more than one item in the queue that is less than the renderTime. Under
331- // this scenario, we just want to continue pulling items from the queue until the last item pulled from the
332- // queue is greater than the redner time or greater than the currently targeted item's sent time.
333- if ( noStateSet || ( ( currentTargetTimeReached || InterpolateState . TargetReached ) && potentialItemNeedsProcessing ) )
325+ // If we haven't set a target or we have another item that needs processing.
326+ if ( noStateSet || potentialItemNeedsProcessing )
334327 {
335328 if ( m_BufferQueue . TryDequeue ( out BufferedItem target ) )
336329 {
337- dequeuedCount ++ ;
338330 if ( ! InterpolateState . Target . HasValue )
339331 {
340332 InterpolateState . Target = target ;
@@ -359,15 +351,20 @@ private void TryConsumeFromBuffer(double renderTime, double minDeltaTime, double
359351 startTime = InterpolateState . Target . Value . TimeSent ;
360352 InterpolateState . MaxDeltaTime = maxDeltaTime ;
361353 InterpolateState . PredictingNext = m_BufferQueue . Count > 0 ;
362- InterpolateState . Phase1Value = InterpolateState . PreviousValue ;
363- if ( extrapolateAhead && InterpolateState . PredictingNext )
354+ if ( lerpAhead )
364355 {
365- InterpolateState . Phase2Value = InterpolateState . PredictingNext ? Interpolate ( InterpolateState . PredictValue , target . Item , InterpolateState . CurrentDeltaTime )
366- : InterpolateState . Phase2Value = InterpolateState . PredictValue ;
356+ InterpolateState . Phase1Value = InterpolateState . PreviousValue ;
357+ if ( InterpolateState . PredictingNext && m_BufferQueue . TryPeek ( out BufferedItem nextTarget ) )
358+ {
359+ InterpolateState . Phase2Value = Interpolate ( InterpolateState . PredictValue , nextTarget . Item , InterpolateState . CurrentDeltaTime ) ;
360+ }
361+ else
362+ {
363+ InterpolateState . Phase2Value = InterpolateState . PredictValue ;
364+ }
367365 }
368366 }
369-
370- InterpolateState . SetTimeToTarget ( Math . Max ( ( float ) ( target . TimeSent - startTime ) , minDeltaTime ) ) ;
367+ InterpolateState . SetTimeToTarget ( Math . Max ( target . TimeSent - startTime , minDeltaTime ) ) ;
371368 InterpolateState . Target = target ;
372369 }
373370 }
@@ -406,11 +403,11 @@ internal void ResetCurrentState()
406403 /// <param name="tickLatencyAsTime">The tick latency in relative local time.</param>
407404 /// <param name="minDeltaTime">The minimum time delta between the current and target value.</param>
408405 /// <param name="maxDeltaTime">The maximum time delta between the current and target value.</param>
409- /// <param name="isLerpAndExtrapolate ">Determines whether to use smooth dampening or extrapolation .</param>
406+ /// <param name="lerpAhead ">Determines whether to use smooth dampening or lerp ahead interpolation type .</param>
410407 /// <returns>The newly interpolated value of type 'T'</returns>
411- internal T Update ( float deltaTime , double tickLatencyAsTime , double minDeltaTime , double maxDeltaTime , bool isLerpAndExtrapolate )
408+ internal T Update ( float deltaTime , double tickLatencyAsTime , double minDeltaTime , double maxDeltaTime , bool lerpAhead )
412409 {
413- TryConsumeFromBuffer ( tickLatencyAsTime , minDeltaTime , maxDeltaTime , isLerpAndExtrapolate ) ;
410+ TryConsumeFromBuffer ( tickLatencyAsTime , minDeltaTime , maxDeltaTime , lerpAhead ) ;
414411 // Only begin interpolation when there is a start and end point
415412 if ( InterpolateState . Target . HasValue )
416413 {
@@ -421,20 +418,21 @@ internal T Update(float deltaTime, double tickLatencyAsTime, double minDeltaTime
421418 // Also calculates the LerpT and LerpTPredicted values.
422419 InterpolateState . AddDeltaTime ( deltaTime ) ;
423420 var targetValue = InterpolateState . CurrentValue ;
424- // SmoothDampen or LerpExtrapolateBlend
425- if ( ! isLerpAndExtrapolate )
421+ // SmoothDampen
422+ if ( ! lerpAhead )
426423 {
427424 InterpolateState . PreviousValue = SmoothDamp ( InterpolateState . PreviousValue , InterpolateState . Target . Value . Item , ref m_RateOfChange , ( float ) InterpolateState . TimeToTargetValue , ( float ) InterpolateState . TimeToTargetValue * InterpolateState . LerpT ) ;
428425 InterpolateState . PredictValue = SmoothDamp ( InterpolateState . PredictValue , InterpolateState . Target . Value . Item , ref m_PredictedRateOfChange , ( float ) InterpolateState . TimeToTargetValue , ( float ) ( InterpolateState . TimeToTargetValue * InterpolateState . LerpTPredict ) ) ;
429426 }
430- else
427+ else // Lerp Ahead
431428 {
432429 InterpolateState . PreviousValue = Interpolate ( InterpolateState . Phase1Value , InterpolateState . Target . Value . Item , InterpolateState . LerpT ) ;
433430 // Note: InterpolateState.LerpTPredict is clamped to LerpT if we have no next target
434431 InterpolateState . PredictValue = InterpolateUnclamped ( InterpolateState . Phase2Value , InterpolateState . Target . Value . Item , InterpolateState . LerpTPredict ) ;
432+
435433 }
436-
437- // Lerp between the PreviousValue and PredictedValue using the calculated time delta
434+
435+ // Lerp between the PreviousValue and PredictedValue using the current delta time
438436 targetValue = Interpolate ( InterpolateState . PreviousValue , InterpolateState . PredictValue , deltaTime ) ;
439437
440438 // If lerp smoothing is enabled, then smooth current value towards the target value
@@ -477,59 +475,58 @@ internal T Update(float deltaTime, double tickLatencyAsTime, double minDeltaTime
477475 /// This version of TryConsumeFromBuffer adheres to the original BufferedLinearInterpolator buffer consumption pattern.
478476 /// </remarks>
479477 /// <param name="renderTime"></param>
480- /// <param name="serverTime"></param>
481- private void TryConsumeFromBuffer ( double renderTime , double serverTime )
478+ private void TryConsumeFromBuffer ( double renderTime )
482479 {
483- if ( ! InterpolateState . Target . HasValue || ( InterpolateState . Target . Value . TimeSent <= renderTime ) )
480+ BufferedItem ? previousItem = null ;
481+ var alreadyHasBufferItem = false ;
482+ while ( m_BufferQueue . TryPeek ( out BufferedItem potentialItem ) )
484483 {
485- BufferedItem ? previousItem = null ;
486- var alreadyHasBufferItem = false ;
487- while ( m_BufferQueue . TryPeek ( out BufferedItem potentialItem ) )
484+ // If we are still on the same buffered item (FIFO Queue), then exit early as there is nothing
485+ // to consume.
486+ if ( previousItem . HasValue && previousItem . Value . TimeSent == potentialItem . TimeSent )
488487 {
489- // If we are still on the same buffered item (FIFO Queue), then exit early as there is nothing
490- // to consume.
491- if ( previousItem . HasValue && previousItem . Value . TimeSent == potentialItem . TimeSent )
492- {
493- break ;
494- }
488+ break ;
489+ }
495490
496- if ( ( potentialItem . TimeSent <= serverTime ) &&
497- ( ! InterpolateState . Target . HasValue || InterpolateState . Target . Value . TimeSent < potentialItem . TimeSent ) )
491+ if ( ( potentialItem . TimeSent <= renderTime ) &&
492+ ( ! InterpolateState . Target . HasValue || InterpolateState . Target . Value . TimeSent < potentialItem . TimeSent ) )
493+ {
494+ if ( m_BufferQueue . TryDequeue ( out BufferedItem target ) )
498495 {
499- if ( m_BufferQueue . TryDequeue ( out BufferedItem target ) )
496+ if ( ! InterpolateState . Target . HasValue )
497+ {
498+ InterpolateState . Target = target ;
499+ alreadyHasBufferItem = true ;
500+ InterpolateState . PreviousValue = InterpolateState . CurrentValue ;
501+ InterpolateState . StartTime = target . TimeSent ;
502+ InterpolateState . EndTime = target . TimeSent ;
503+ InterpolateState . TargetReached = false ;
504+ }
505+ else
500506 {
501- if ( ! InterpolateState . Target . HasValue )
507+ if ( ! alreadyHasBufferItem )
502508 {
503- InterpolateState . Target = target ;
504509 alreadyHasBufferItem = true ;
510+ InterpolateState . StartTime = InterpolateState . Target . Value . TimeSent ;
505511 InterpolateState . PreviousValue = InterpolateState . CurrentValue ;
506- InterpolateState . StartTime = target . TimeSent ;
507- InterpolateState . EndTime = target . TimeSent ;
508- }
509- else
510- {
511- if ( ! alreadyHasBufferItem )
512- {
513- alreadyHasBufferItem = true ;
514- InterpolateState . StartTime = InterpolateState . Target . Value . TimeSent ;
515- InterpolateState . PreviousValue = InterpolateState . CurrentValue ;
516- }
517- InterpolateState . EndTime = target . TimeSent ;
518- InterpolateState . Target = target ;
512+ InterpolateState . TargetReached = false ;
519513 }
514+ InterpolateState . EndTime = target . TimeSent ;
515+ InterpolateState . Target = target ;
516+ InterpolateState . TimeToTargetValue = InterpolateState . EndTime - InterpolateState . StartTime ;
520517 }
521518 }
522- else
523- {
524- break ;
525- }
519+ }
520+ else
521+ {
522+ break ;
523+ }
526524
527- if ( ! InterpolateState . Target . HasValue )
528- {
529- break ;
530- }
531- previousItem = potentialItem ;
525+ if ( ! InterpolateState . Target . HasValue )
526+ {
527+ break ;
532528 }
529+ previousItem = potentialItem ;
533530 }
534531 }
535532
@@ -542,31 +539,22 @@ private void TryConsumeFromBuffer(double renderTime, double serverTime)
542539 /// <param name="deltaTime">time since last call</param>
543540 /// <param name="renderTime">our current time</param>
544541 /// <param name="serverTime">current server time</param>
542+ /// <remarks>
543+ /// We no longer use serverTime.
544+ /// </remarks>
545545 /// <returns>The newly interpolated value of type 'T'</returns>
546546 public T Update ( float deltaTime , double renderTime , double serverTime )
547547 {
548- TryConsumeFromBuffer ( renderTime , serverTime ) ;
548+ TryConsumeFromBuffer ( renderTime ) ;
549549 // Only interpolate when there is a start and end point and we have not already reached the end value
550- if ( InterpolateState . Target . HasValue )
550+ if ( InterpolateState . Target . HasValue && ! InterpolateState . TargetReached )
551551 {
552552 // The original BufferedLinearInterpolator lerping script to assure the Smooth Dampening updates do not impact
553553 // this specific behavior.
554554 float t = 1.0f ;
555- InterpolateState . TimeToTargetValue = InterpolateState . EndTime - InterpolateState . StartTime ;
556555 if ( InterpolateState . TimeToTargetValue > k_SmallValue )
557556 {
558- t = ( float ) ( ( renderTime - InterpolateState . StartTime ) / InterpolateState . TimeToTargetValue ) ;
559-
560- if ( t < 0.0f )
561- {
562- t = 0.0f ;
563- }
564-
565- if ( t > MaxInterpolationBound ) // max extrapolation
566- {
567- // TODO this causes issues with teleport, investigate
568- t = 1.0f ;
569- }
557+ t = Math . Clamp ( ( float ) ( ( renderTime - InterpolateState . StartTime ) / InterpolateState . TimeToTargetValue ) , 0.0f , 1.0f ) ;
570558 }
571559 var target = Interpolate ( InterpolateState . PreviousValue , InterpolateState . Target . Value . Item , t ) ;
572560
@@ -579,6 +567,22 @@ public T Update(float deltaTime, double renderTime, double serverTime)
579567 {
580568 InterpolateState . CurrentValue = target ;
581569 }
570+ // Determine if we have reached our target
571+ InterpolateState . TargetReached = IsAproximately ( InterpolateState . CurrentValue , InterpolateState . Target . Value . Item ) ;
572+ }
573+ else // If the target is reached and we have no more state updates, we want to check to see if we need to reset.
574+ if ( m_BufferQueue . Count == 0 && InterpolateState . TargetReached )
575+ {
576+ // When the delta between the time sent and the current tick latency time-window is greater than the max delta time
577+ // plus the minimum delta time (a rough estimate of time to wait before we consider rate of change equal to zero),
578+ // we will want to reset the interpolator with the current known value. This prevents the next received state update's
579+ // time to be calculated against the last calculated time which if there is an extended period of time between the two
580+ // it would cause a large delta time period between the two states (i.e. it stops moving for a second or two and then
581+ // starts moving again).
582+ if ( ( renderTime - InterpolateState . Target . Value . TimeSent ) > 0.3f ) // If we haven't recevied anything within 300ms, assume we stopped motion.
583+ {
584+ InterpolateState . Reset ( InterpolateState . CurrentValue ) ;
585+ }
582586 }
583587 m_NbItemsReceivedThisFrame = 0 ;
584588 return InterpolateState . CurrentValue ;
0 commit comments