@@ -34,6 +34,8 @@ pub struct Time {
3434 raw_elapsed_wrapped : Duration ,
3535 raw_elapsed_seconds_wrapped : f32 ,
3636 raw_elapsed_seconds_wrapped_f64 : f64 ,
37+ // maximum delta
38+ maximum_delta : Option < Duration > ,
3739}
3840
3941impl Default for Time {
@@ -63,6 +65,7 @@ impl Default for Time {
6365 raw_elapsed_wrapped : Duration :: ZERO ,
6466 raw_elapsed_seconds_wrapped : 0.0 ,
6567 raw_elapsed_seconds_wrapped_f64 : 0.0 ,
68+ maximum_delta : Some ( Duration :: from_millis ( 333 ) ) , // 0.333 seconds
6669 }
6770 }
6871}
@@ -139,13 +142,20 @@ impl Time {
139142 /// ```
140143 pub fn update_with_instant ( & mut self , instant : Instant ) {
141144 let raw_delta = instant - self . last_update . unwrap_or ( self . startup ) ;
145+ // if time jumps forward significantly, clamp maximum delta added to
146+ // elapsed (but not raw_elapsed), before relative speed scaling
147+ let clamped_delta = if let Some ( maximum_delta) = self . maximum_delta {
148+ std:: cmp:: min ( raw_delta, maximum_delta)
149+ } else {
150+ raw_delta
151+ } ;
142152 let delta = if self . paused {
143153 Duration :: ZERO
144154 } else if self . relative_speed != 1.0 {
145- raw_delta . mul_f64 ( self . relative_speed )
155+ clamped_delta . mul_f64 ( self . relative_speed )
146156 } else {
147157 // avoid rounding when at normal speed
148- raw_delta
158+ clamped_delta
149159 } ;
150160
151161 if self . last_update . is_some ( ) {
@@ -351,6 +361,29 @@ impl Time {
351361 self . wrap_period = wrap_period;
352362 }
353363
364+ /// Returns the maximum time [`elapsed`](#method.elapsed) can be increased
365+ /// in a single update.
366+ ///
367+ /// This is meant to avoid glitches in game logic if the isn't updated in a
368+ /// while due to some external reason.
369+ ///
370+ /// **Note:** The default is 0.333 seconds.
371+ #[ inline]
372+ pub fn maximum_delta ( & self ) -> Option < Duration > {
373+ self . maximum_delta
374+ }
375+
376+ /// Sets the maximum time [`elapsed`](#method.elapsed) can be increased in a
377+ /// single update.
378+ ///
379+ /// Setting `None` will disable the maximum delta limit, which should be
380+ /// used with care, as it may provoke undesirable behaviour in systems that
381+ /// observe time.
382+ #[ inline]
383+ pub fn set_maximum_delta ( & mut self , maximum_delta : Option < Duration > ) {
384+ self . maximum_delta = maximum_delta;
385+ }
386+
354387 /// Returns the speed the clock advances relative to your system clock, as [`f32`].
355388 /// This is known as "time scaling" or "time dilation" in other engines.
356389 ///
@@ -463,6 +496,7 @@ mod tests {
463496 assert_eq ! ( time. raw_elapsed( ) , Duration :: ZERO ) ;
464497 assert_eq ! ( time. raw_elapsed_seconds( ) , 0.0 ) ;
465498 assert_eq ! ( time. raw_elapsed_seconds_f64( ) , 0.0 ) ;
499+ assert_eq ! ( time. maximum_delta( ) , Some ( Duration :: from_millis( 333 ) ) ) ;
466500
467501 // Update `time` and check results.
468502 // The first update to `time` normally happens before other systems have run,
@@ -555,6 +589,7 @@ mod tests {
555589 let mut time = Time {
556590 startup : start_instant,
557591 wrap_period : Duration :: from_secs ( 3 ) ,
592+ maximum_delta : Some ( Duration :: from_secs ( 5 ) ) ,
558593 ..Default :: default ( )
559594 } ;
560595
@@ -632,8 +667,8 @@ mod tests {
632667 // Make app time advance at 2x the rate of your system clock.
633668 time. set_relative_speed ( 2.0 ) ;
634669
635- // Update `time` again 1 second later.
636- let elapsed = Duration :: from_secs ( 1 ) ;
670+ // Update `time` again 250 milliseconds later.
671+ let elapsed = Duration :: from_millis ( 250 ) ;
637672 let third_update_instant = second_update_instant + elapsed;
638673 time. update_with_instant ( third_update_instant) ;
639674
@@ -725,4 +760,41 @@ mod tests {
725760 ) ;
726761 assert_eq ! ( time. raw_elapsed( ) , third_update_instant - start_instant) ;
727762 }
763+
764+ #[ test]
765+ fn maximum_delta_test ( ) {
766+ let start_instant = Instant :: now ( ) ;
767+
768+ let mut time = Time :: new ( start_instant) ;
769+
770+ assert_eq ! ( time. elapsed_seconds( ) , 0.0 ) ;
771+
772+ time. update_with_instant ( start_instant + Duration :: from_millis ( 333 ) ) ;
773+ assert_float_eq ( time. elapsed_seconds ( ) , 0.333 ) ;
774+
775+ time. update_with_instant ( start_instant + Duration :: from_millis ( 1000 ) ) ;
776+ assert_float_eq ( time. elapsed_seconds ( ) , 0.666 ) ;
777+
778+ time. update_with_instant ( start_instant + Duration :: from_millis ( 4999 ) ) ;
779+ assert_float_eq ( time. elapsed_seconds ( ) , 0.999 ) ;
780+
781+ time. update_with_instant ( start_instant + Duration :: from_millis ( 5000 ) ) ;
782+ assert_float_eq ( time. elapsed_seconds ( ) , 1.0 ) ;
783+
784+ time. set_maximum_delta ( Some ( Duration :: from_secs ( 10 ) ) ) ;
785+
786+ time. update_with_instant ( start_instant + Duration :: from_secs ( 6 ) ) ;
787+ assert_float_eq ( time. elapsed_seconds ( ) , 2.0 ) ;
788+
789+ time. update_with_instant ( start_instant + Duration :: from_secs ( 16 ) ) ;
790+ assert_float_eq ( time. elapsed_seconds ( ) , 12.0 ) ;
791+
792+ time. update_with_instant ( start_instant + Duration :: from_secs ( 30 ) ) ;
793+ assert_float_eq ( time. elapsed_seconds ( ) , 22.0 ) ;
794+
795+ time. set_maximum_delta ( None ) ;
796+
797+ time. update_with_instant ( start_instant + Duration :: from_secs ( 60 ) ) ;
798+ assert_float_eq ( time. elapsed_seconds ( ) , 52.0 ) ;
799+ }
728800}
0 commit comments