#[cfg(feature = "bevy_reflect")]1use bevy_reflect::Reflect;2use core::time::Duration;3use log::debug;45use crate::{real::Real, time::Time};67/// The virtual game clock representing game time.8///9/// A specialization of the [`Time`] structure. **For method documentation, see10/// [`Time<Virtual>#impl-Time<Virtual>`].**11///12/// Normally used as `Time<Virtual>`. It is automatically inserted as a resource13/// by [`TimePlugin`](crate::TimePlugin) and updated based on14/// [`Time<Real>`](Real). The virtual clock is automatically set as the default15/// generic [`Time`] resource for the update.16///17/// The virtual clock differs from real time clock in that it can be paused, sped up18/// and slowed down. It also limits how much it can advance in a single update19/// in order to prevent unexpected behavior in cases where updates do not happen20/// at regular intervals (e.g. coming back after the program was suspended a long time).21///22/// The virtual clock can be paused by calling [`pause()`](Time::pause) and23/// unpaused by calling [`unpause()`](Time::unpause). When the game clock is24/// paused [`delta()`](Time::delta) will be zero on each update, and25/// [`elapsed()`](Time::elapsed) will not grow.26/// [`effective_speed()`](Time::effective_speed) will return `0.0`. Calling27/// [`pause()`](Time::pause) will not affect value the [`delta()`](Time::delta)28/// value for the update currently being processed.29///30/// The speed of the virtual clock can be changed by calling31/// [`set_relative_speed()`](Time::set_relative_speed). A value of `2.0` means32/// that virtual clock should advance twice as fast as real time, meaning that33/// [`delta()`](Time::delta) values will be double of what34/// [`Time<Real>::delta()`](Time::delta) reports and35/// [`elapsed()`](Time::elapsed) will go twice as fast as36/// [`Time<Real>::elapsed()`](Time::elapsed). Calling37/// [`set_relative_speed()`](Time::set_relative_speed) will not affect the38/// [`delta()`](Time::delta) value for the update currently being processed.39///40/// The maximum amount of delta time that can be added by a single update can be41/// set by [`set_max_delta()`](Time::set_max_delta). This value serves a dual42/// purpose in the virtual clock.43///44/// If the game temporarily freezes due to any reason, such as disk access, a45/// blocking system call, or operating system level suspend, reporting the full46/// elapsed delta time is likely to cause bugs in game logic. Usually if a47/// laptop is suspended for an hour, it doesn't make sense to try to simulate48/// the game logic for the elapsed hour when resuming. Instead it is better to49/// lose the extra time and pretend a shorter duration of time passed. Setting50/// [`max_delta()`](Time::max_delta) to a relatively short time means that the51/// impact on game logic will be minimal.52///53/// If the game lags for some reason, meaning that it will take a longer time to54/// compute a frame than the real time that passes during the computation, then55/// we would fall behind in processing virtual time. If this situation persists,56/// and computing a frame takes longer depending on how much virtual time has57/// passed, the game would enter a "death spiral" where computing each frame58/// takes longer and longer and the game will appear to freeze. By limiting the59/// maximum time that can be added at once, we also limit the amount of virtual60/// time the game needs to compute for each frame. This means that the game will61/// run slow, and it will run slower than real time, but it will not freeze and62/// it will recover as soon as computation becomes fast again.63///64/// You should set [`max_delta()`](Time::max_delta) to a value that is65/// approximately the minimum FPS your game should have even if heavily lagged66/// for a moment. The actual FPS when lagged will be somewhat lower than this,67/// depending on how much more time it takes to compute a frame compared to real68/// time. You should also consider how stable your FPS is, as the limit will69/// also dictate how big of an FPS drop you can accept without losing time and70/// falling behind real time.71#[derive(Debug, Copy, Clone)]72#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Clone))]73pub struct Virtual {74max_delta: Duration,75paused: bool,76relative_speed: f64,77effective_speed: f64,78}7980impl Time<Virtual> {81/// The default amount of time that can added in a single update.82///83/// Equal to 250 milliseconds.84const DEFAULT_MAX_DELTA: Duration = Duration::from_millis(250);8586/// Create new virtual clock with given maximum delta step [`Duration`]87///88/// # Panics89///90/// Panics if `max_delta` is zero.91pub fn from_max_delta(max_delta: Duration) -> Self {92let mut ret = Self::default();93ret.set_max_delta(max_delta);94ret95}9697/// Returns the maximum amount of time that can be added to this clock by a98/// single update, as [`Duration`].99///100/// This is the maximum value [`Self::delta()`] will return and also to101/// maximum time [`Self::elapsed()`] will be increased by in a single102/// update.103///104/// This ensures that even if no updates happen for an extended amount of time,105/// the clock will not have a sudden, huge advance all at once. This also indirectly106/// limits the maximum number of fixed update steps that can run in a single update.107///108/// The default value is 250 milliseconds.109#[inline]110pub fn max_delta(&self) -> Duration {111self.context().max_delta112}113114/// Sets the maximum amount of time that can be added to this clock by a115/// single update, as [`Duration`].116///117/// This is the maximum value [`Self::delta()`] will return and also to118/// maximum time [`Self::elapsed()`] will be increased by in a single119/// update.120///121/// This is used to ensure that even if the game freezes for a few seconds,122/// or is suspended for hours or even days, the virtual clock doesn't123/// suddenly jump forward for that full amount, which would likely cause124/// gameplay bugs or having to suddenly simulate all the intervening time.125///126/// If no updates happen for an extended amount of time, this limit prevents127/// having a sudden, huge advance all at once. This also indirectly limits128/// the maximum number of fixed update steps that can run in a single129/// update.130///131/// The default value is 250 milliseconds. If you want to disable this132/// feature, set the value to [`Duration::MAX`].133///134/// # Panics135///136/// Panics if `max_delta` is zero.137#[inline]138pub fn set_max_delta(&mut self, max_delta: Duration) {139assert_ne!(max_delta, Duration::ZERO, "tried to set max delta to zero");140self.context_mut().max_delta = max_delta;141}142143/// Returns the speed the clock advances relative to your system clock, as [`f32`].144/// This is known as "time scaling" or "time dilation" in other engines.145#[inline]146pub fn relative_speed(&self) -> f32 {147self.relative_speed_f64() as f32148}149150/// Returns the speed the clock advances relative to your system clock, as [`f64`].151/// This is known as "time scaling" or "time dilation" in other engines.152#[inline]153pub fn relative_speed_f64(&self) -> f64 {154self.context().relative_speed155}156157/// Returns the speed the clock advanced relative to your system clock in158/// this update, as [`f32`].159///160/// Returns `0.0` if the game was paused or what the `relative_speed` value161/// was at the start of this update.162#[inline]163pub fn effective_speed(&self) -> f32 {164self.context().effective_speed as f32165}166167/// Returns the speed the clock advanced relative to your system clock in168/// this update, as [`f64`].169///170/// Returns `0.0` if the game was paused or what the `relative_speed` value171/// was at the start of this update.172#[inline]173pub fn effective_speed_f64(&self) -> f64 {174self.context().effective_speed175}176177/// Sets the speed the clock advances relative to your system clock, given as an [`f32`].178///179/// For example, setting this to `2.0` will make the clock advance twice as fast as your system180/// clock.181///182/// # Panics183///184/// Panics if `ratio` is negative or not finite.185#[inline]186pub fn set_relative_speed(&mut self, ratio: f32) {187self.set_relative_speed_f64(ratio as f64);188}189190/// Sets the speed the clock advances relative to your system clock, given as an [`f64`].191///192/// For example, setting this to `2.0` will make the clock advance twice as fast as your system193/// clock.194///195/// # Panics196///197/// Panics if `ratio` is negative or not finite.198#[inline]199pub fn set_relative_speed_f64(&mut self, ratio: f64) {200assert!(ratio.is_finite(), "tried to go infinitely fast");201assert!(ratio >= 0.0, "tried to go back in time");202self.context_mut().relative_speed = ratio;203}204205/// Stops the clock, preventing it from advancing until resumed.206#[inline]207pub fn pause(&mut self) {208self.context_mut().paused = true;209}210211/// Resumes the clock if paused.212#[inline]213pub fn unpause(&mut self) {214self.context_mut().paused = false;215}216217/// Returns `true` if the clock is currently paused.218#[inline]219pub fn is_paused(&self) -> bool {220self.context().paused221}222223/// Returns `true` if the clock was paused at the start of this update.224#[inline]225pub fn was_paused(&self) -> bool {226self.context().effective_speed == 0.0227}228229/// Updates the elapsed duration of `self` by `raw_delta`, up to the `max_delta`.230fn advance_with_raw_delta(&mut self, raw_delta: Duration) {231let max_delta = self.context().max_delta;232let clamped_delta = if raw_delta > max_delta {233debug!(234"delta time larger than maximum delta, clamping delta to {:?} and skipping {:?}",235max_delta,236raw_delta - max_delta237);238max_delta239} else {240raw_delta241};242let effective_speed = if self.context().paused {2430.0244} else {245self.context().relative_speed246};247let delta = if effective_speed != 1.0 {248clamped_delta.mul_f64(effective_speed)249} else {250// avoid rounding when at normal speed251clamped_delta252};253self.context_mut().effective_speed = effective_speed;254self.advance_by(delta);255}256}257258impl Default for Virtual {259fn default() -> Self {260Self {261max_delta: Time::<Virtual>::DEFAULT_MAX_DELTA,262paused: false,263relative_speed: 1.0,264effective_speed: 1.0,265}266}267}268269/// Advances [`Time<Virtual>`] and [`Time`] based on the elapsed [`Time<Real>`].270///271/// The virtual time will be advanced up to the provided [`Time::max_delta`].272pub fn update_virtual_time(current: &mut Time, virt: &mut Time<Virtual>, real: &Time<Real>) {273let raw_delta = real.delta();274virt.advance_with_raw_delta(raw_delta);275*current = virt.as_generic();276}277278#[cfg(test)]279mod test {280use super::*;281282#[test]283fn test_default() {284let time = Time::<Virtual>::default();285286assert!(!time.is_paused()); // false287assert_eq!(time.relative_speed(), 1.0);288assert_eq!(time.max_delta(), Time::<Virtual>::DEFAULT_MAX_DELTA);289assert_eq!(time.delta(), Duration::ZERO);290assert_eq!(time.elapsed(), Duration::ZERO);291}292293#[test]294fn test_advance() {295let mut time = Time::<Virtual>::default();296297time.advance_with_raw_delta(Duration::from_millis(125));298299assert_eq!(time.delta(), Duration::from_millis(125));300assert_eq!(time.elapsed(), Duration::from_millis(125));301302time.advance_with_raw_delta(Duration::from_millis(125));303304assert_eq!(time.delta(), Duration::from_millis(125));305assert_eq!(time.elapsed(), Duration::from_millis(250));306307time.advance_with_raw_delta(Duration::from_millis(125));308309assert_eq!(time.delta(), Duration::from_millis(125));310assert_eq!(time.elapsed(), Duration::from_millis(375));311312time.advance_with_raw_delta(Duration::from_millis(125));313314assert_eq!(time.delta(), Duration::from_millis(125));315assert_eq!(time.elapsed(), Duration::from_millis(500));316}317318#[test]319fn test_relative_speed() {320let mut time = Time::<Virtual>::default();321322time.advance_with_raw_delta(Duration::from_millis(250));323324assert_eq!(time.relative_speed(), 1.0);325assert_eq!(time.effective_speed(), 1.0);326assert_eq!(time.delta(), Duration::from_millis(250));327assert_eq!(time.elapsed(), Duration::from_millis(250));328329time.set_relative_speed_f64(2.0);330331assert_eq!(time.relative_speed(), 2.0);332assert_eq!(time.effective_speed(), 1.0);333334time.advance_with_raw_delta(Duration::from_millis(250));335336assert_eq!(time.relative_speed(), 2.0);337assert_eq!(time.effective_speed(), 2.0);338assert_eq!(time.delta(), Duration::from_millis(500));339assert_eq!(time.elapsed(), Duration::from_millis(750));340341time.set_relative_speed_f64(0.5);342343assert_eq!(time.relative_speed(), 0.5);344assert_eq!(time.effective_speed(), 2.0);345346time.advance_with_raw_delta(Duration::from_millis(250));347348assert_eq!(time.relative_speed(), 0.5);349assert_eq!(time.effective_speed(), 0.5);350assert_eq!(time.delta(), Duration::from_millis(125));351assert_eq!(time.elapsed(), Duration::from_millis(875));352}353354#[test]355fn test_pause() {356let mut time = Time::<Virtual>::default();357358time.advance_with_raw_delta(Duration::from_millis(250));359360assert!(!time.is_paused()); // false361assert!(!time.was_paused()); // false362assert_eq!(time.relative_speed(), 1.0);363assert_eq!(time.effective_speed(), 1.0);364assert_eq!(time.delta(), Duration::from_millis(250));365assert_eq!(time.elapsed(), Duration::from_millis(250));366367time.pause();368369assert!(time.is_paused()); // true370assert!(!time.was_paused()); // false371assert_eq!(time.relative_speed(), 1.0);372assert_eq!(time.effective_speed(), 1.0);373374time.advance_with_raw_delta(Duration::from_millis(250));375376assert!(time.is_paused()); // true377assert!(time.was_paused()); // true378assert_eq!(time.relative_speed(), 1.0);379assert_eq!(time.effective_speed(), 0.0);380assert_eq!(time.delta(), Duration::ZERO);381assert_eq!(time.elapsed(), Duration::from_millis(250));382383time.unpause();384385assert!(!time.is_paused()); // false386assert!(time.was_paused()); // true387assert_eq!(time.relative_speed(), 1.0);388assert_eq!(time.effective_speed(), 0.0);389390time.advance_with_raw_delta(Duration::from_millis(250));391392assert!(!time.is_paused()); // false393assert!(!time.was_paused()); // false394assert_eq!(time.relative_speed(), 1.0);395assert_eq!(time.effective_speed(), 1.0);396assert_eq!(time.delta(), Duration::from_millis(250));397assert_eq!(time.elapsed(), Duration::from_millis(500));398}399400#[test]401fn test_max_delta() {402let mut time = Time::<Virtual>::default();403time.set_max_delta(Duration::from_millis(500));404405time.advance_with_raw_delta(Duration::from_millis(250));406407assert_eq!(time.delta(), Duration::from_millis(250));408assert_eq!(time.elapsed(), Duration::from_millis(250));409410time.advance_with_raw_delta(Duration::from_millis(500));411412assert_eq!(time.delta(), Duration::from_millis(500));413assert_eq!(time.elapsed(), Duration::from_millis(750));414415time.advance_with_raw_delta(Duration::from_millis(750));416417assert_eq!(time.delta(), Duration::from_millis(500));418assert_eq!(time.elapsed(), Duration::from_millis(1250));419420time.set_max_delta(Duration::from_secs(1));421422assert_eq!(time.max_delta(), Duration::from_secs(1));423424time.advance_with_raw_delta(Duration::from_millis(750));425426assert_eq!(time.delta(), Duration::from_millis(750));427assert_eq!(time.elapsed(), Duration::from_millis(2000));428429time.advance_with_raw_delta(Duration::from_millis(1250));430431assert_eq!(time.delta(), Duration::from_millis(1000));432assert_eq!(time.elapsed(), Duration::from_millis(3000));433}434}435436437