use bevy_ecs::resource::Resource;1use core::time::Duration;2#[cfg(feature = "bevy_reflect")]3use {4bevy_ecs::reflect::ReflectResource,5bevy_reflect::{std_traits::ReflectDefault, Reflect},6};78/// A generic clock resource that tracks how much it has advanced since its9/// previous update and since its creation.10///11/// Multiple instances of this resource are inserted automatically by12/// [`TimePlugin`](crate::TimePlugin):13///14/// - [`Time<Real>`](crate::real::Real) tracks real wall-clock time elapsed.15/// - [`Time<Virtual>`](crate::virt::Virtual) tracks virtual game time that may16/// be paused or scaled.17/// - [`Time<Fixed>`](crate::fixed::Fixed) tracks fixed timesteps based on18/// virtual time.19/// - [`Time`] is a generic clock that corresponds to "current" or "default"20/// time for systems. It contains [`Time<Virtual>`](crate::virt::Virtual)21/// except inside the [`FixedMain`](bevy_app::FixedMain) schedule when it22/// contains [`Time<Fixed>`](crate::fixed::Fixed).23///24/// The time elapsed since the previous time this clock was advanced is saved as25/// [`delta()`](Time::delta) and the total amount of time the clock has advanced26/// is saved as [`elapsed()`](Time::elapsed). Both are represented as exact27/// [`Duration`] values with fixed nanosecond precision. The clock does not28/// support time moving backwards, but it can be updated with [`Duration::ZERO`]29/// which will set [`delta()`](Time::delta) to zero.30///31/// These values are also available in seconds as `f32` via32/// [`delta_secs()`](Time::delta_secs) and33/// [`elapsed_secs()`](Time::elapsed_secs), and also in seconds as `f64`34/// via [`delta_secs_f64()`](Time::delta_secs_f64) and35/// [`elapsed_secs_f64()`](Time::elapsed_secs_f64).36///37/// Since [`elapsed_secs()`](Time::elapsed_secs) will grow constantly and38/// is `f32`, it will exhibit gradual precision loss. For applications that39/// require an `f32` value but suffer from gradual precision loss there is40/// [`elapsed_secs_wrapped()`](Time::elapsed_secs_wrapped) available. The41/// same wrapped value is also available as [`Duration`] and `f64` for42/// consistency. The wrap period is by default 1 hour, and can be set by43/// [`set_wrap_period()`](Time::set_wrap_period).44///45/// # Accessing clocks46///47/// By default, any systems requiring current [`delta()`](Time::delta) or48/// [`elapsed()`](Time::elapsed) should use `Res<Time>` to access the default49/// time configured for the program. By default, this refers to50/// [`Time<Virtual>`](crate::virt::Virtual) except during the51/// [`FixedMain`](bevy_app::FixedMain) schedule when it refers to52/// [`Time<Fixed>`](crate::fixed::Fixed). This ensures your system can be used53/// either in [`Update`](bevy_app::Update) or54/// [`FixedUpdate`](bevy_app::FixedUpdate) schedule depending on what is needed.55///56/// ```57/// # use bevy_ecs::prelude::*;58/// # use bevy_time::prelude::*;59/// #60/// fn ambivalent_system(time: Res<Time>) {61/// println!("this how I see time: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());62/// }63/// ```64///65/// If your system needs to react based on real time (wall clock time), like for66/// user interfaces, it should use `Res<Time<Real>>`. The67/// [`delta()`](Time::delta) and [`elapsed()`](Time::elapsed) values will always68/// correspond to real time and will not be affected by pause, time scaling or69/// other tweaks.70///71/// ```72/// # use bevy_ecs::prelude::*;73/// # use bevy_time::prelude::*;74/// #75/// fn real_time_system(time: Res<Time<Real>>) {76/// println!("this will always be real time: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());77/// }78/// ```79///80/// If your system specifically needs to access fixed timestep clock, even when81/// placed in `Update` schedule, you should use `Res<Time<Fixed>>`. The82/// [`delta()`](Time::delta) and [`elapsed()`](Time::elapsed) values will83/// correspond to the latest fixed timestep that has been run.84///85/// ```86/// # use bevy_ecs::prelude::*;87/// # use bevy_time::prelude::*;88/// #89/// fn fixed_time_system(time: Res<Time<Fixed>>) {90/// println!("this will always be the last executed fixed timestep: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());91/// }92/// ```93///94/// Finally, if your system specifically needs to know the current virtual game95/// time, even if placed inside [`FixedUpdate`](bevy_app::FixedUpdate), for96/// example to know if the game is [`was_paused()`](Time::was_paused) or to use97/// [`effective_speed()`](Time::effective_speed), you can use98/// `Res<Time<Virtual>>`. However, if the system is placed in99/// [`FixedUpdate`](bevy_app::FixedUpdate), extra care must be used because your100/// system might be run multiple times with the same [`delta()`](Time::delta)101/// and [`elapsed()`](Time::elapsed) values as the virtual game time has not102/// changed between the iterations.103///104/// ```105/// # use bevy_ecs::prelude::*;106/// # use bevy_time::prelude::*;107/// #108/// fn fixed_time_system(time: Res<Time<Virtual>>) {109/// println!("this will be virtual time for this update: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());110/// println!("also the relative speed of the game is now {}", time.effective_speed());111/// }112/// ```113///114/// If you need to change the settings for any of the clocks, for example to115/// [`pause()`](Time::pause) the game, you should use `ResMut<Time<Virtual>>`.116///117/// ```118/// # use bevy_ecs::prelude::*;119/// # use bevy_time::prelude::*;120/// #121/// #[derive(BufferedEvent)]122/// struct PauseEvent(bool);123///124/// fn pause_system(mut time: ResMut<Time<Virtual>>, mut events: EventReader<PauseEvent>) {125/// for ev in events.read() {126/// if ev.0 {127/// time.pause();128/// } else {129/// time.unpause();130/// }131/// }132/// }133/// ```134///135/// # Adding custom clocks136///137/// New custom clocks can be created by creating your own struct as a context138/// and passing it to [`new_with()`](Time::new_with). These clocks can be139/// inserted as resources as normal and then accessed by systems. You can use140/// the [`advance_by()`](Time::advance_by) or [`advance_to()`](Time::advance_to)141/// methods to move the clock forwards based on your own logic.142///143/// If you want to add methods for your time instance and they require access to144/// both your context and the generic time part, it's probably simplest to add a145/// custom trait for them and implement it for `Time<Custom>`.146///147/// Your context struct will need to implement the [`Default`] trait because148/// [`Time`] structures support reflection. It also makes initialization trivial149/// by being able to call `app.init_resource::<Time<Custom>>()`.150///151/// You can also replace the "generic" `Time` clock resource if the "default"152/// time for your game should not be the default virtual time provided. You can153/// get a "generic" snapshot of your clock by calling `as_generic()` and then154/// overwrite the [`Time`] resource with it. The default systems added by155/// [`TimePlugin`](crate::TimePlugin) will overwrite the [`Time`] clock during156/// [`First`](bevy_app::First) and [`FixedUpdate`](bevy_app::FixedUpdate)157/// schedules.158///159/// ```160/// # use bevy_ecs::prelude::*;161/// # use bevy_time::prelude::*;162/// # use bevy_platform::time::Instant;163/// #164/// #[derive(Debug)]165/// struct Custom {166/// last_external_time: Instant,167/// }168///169/// impl Default for Custom {170/// fn default() -> Self {171/// Self {172/// last_external_time: Instant::now(),173/// }174/// }175/// }176///177/// trait CustomTime {178/// fn update_from_external(&mut self, instant: Instant);179/// }180///181/// impl CustomTime for Time<Custom> {182/// fn update_from_external(&mut self, instant: Instant) {183/// let delta = instant - self.context().last_external_time;184/// self.advance_by(delta);185/// self.context_mut().last_external_time = instant;186/// }187/// }188/// ```189#[derive(Resource, Debug, Copy, Clone)]190#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Resource, Default))]191pub struct Time<T: Default = ()> {192context: T,193wrap_period: Duration,194delta: Duration,195delta_secs: f32,196delta_secs_f64: f64,197elapsed: Duration,198elapsed_secs: f32,199elapsed_secs_f64: f64,200elapsed_wrapped: Duration,201elapsed_secs_wrapped: f32,202elapsed_secs_wrapped_f64: f64,203}204205impl<T: Default> Time<T> {206const DEFAULT_WRAP_PERIOD: Duration = Duration::from_secs(3600); // 1 hour207208/// Create a new clock from context with [`Self::delta`] and [`Self::elapsed`] starting from209/// zero.210pub fn new_with(context: T) -> Self {211Self {212context,213..Default::default()214}215}216217/// Advance this clock by adding a `delta` duration to it.218///219/// The added duration will be returned by [`Self::delta`] and220/// [`Self::elapsed`] will be increased by the duration. Adding221/// [`Duration::ZERO`] is allowed and will set [`Self::delta`] to zero.222pub fn advance_by(&mut self, delta: Duration) {223self.delta = delta;224self.delta_secs = self.delta.as_secs_f32();225self.delta_secs_f64 = self.delta.as_secs_f64();226self.elapsed += delta;227self.elapsed_secs = self.elapsed.as_secs_f32();228self.elapsed_secs_f64 = self.elapsed.as_secs_f64();229self.elapsed_wrapped = duration_rem(self.elapsed, self.wrap_period);230self.elapsed_secs_wrapped = self.elapsed_wrapped.as_secs_f32();231self.elapsed_secs_wrapped_f64 = self.elapsed_wrapped.as_secs_f64();232}233234/// Advance this clock to a specific `elapsed` time.235///236/// [`Self::delta()`] will return the amount of time the clock was advanced237/// and [`Self::elapsed()`] will be the `elapsed` value passed in. Cannot be238/// used to move time backwards.239///240/// # Panics241///242/// Panics if `elapsed` is less than `Self::elapsed()`.243pub fn advance_to(&mut self, elapsed: Duration) {244assert!(245elapsed >= self.elapsed,246"tried to move time backwards to an earlier elapsed moment"247);248self.advance_by(elapsed - self.elapsed);249}250251/// Returns the modulus used to calculate [`elapsed_wrapped`](#method.elapsed_wrapped).252///253/// **Note:** The default modulus is one hour.254#[inline]255pub fn wrap_period(&self) -> Duration {256self.wrap_period257}258259/// Sets the modulus used to calculate [`elapsed_wrapped`](#method.elapsed_wrapped).260///261/// **Note:** This will not take effect until the next update.262///263/// # Panics264///265/// Panics if `wrap_period` is a zero-length duration.266#[inline]267pub fn set_wrap_period(&mut self, wrap_period: Duration) {268assert!(!wrap_period.is_zero(), "division by zero");269self.wrap_period = wrap_period;270}271272/// Returns how much time has advanced since the last [`update`](#method.update), as a273/// [`Duration`].274#[inline]275pub fn delta(&self) -> Duration {276self.delta277}278279/// Returns how much time has advanced since the last [`update`](#method.update), as [`f32`]280/// seconds.281#[inline]282pub fn delta_secs(&self) -> f32 {283self.delta_secs284}285286/// Returns how much time has advanced since the last [`update`](#method.update), as [`f64`]287/// seconds.288#[inline]289pub fn delta_secs_f64(&self) -> f64 {290self.delta_secs_f64291}292293/// Returns how much time has advanced since [`startup`](#method.startup), as [`Duration`].294#[inline]295pub fn elapsed(&self) -> Duration {296self.elapsed297}298299/// Returns how much time has advanced since [`startup`](#method.startup), as [`f32`] seconds.300///301/// **Note:** This is a monotonically increasing value. Its precision will degrade over time.302/// If you need an `f32` but that precision loss is unacceptable,303/// use [`elapsed_secs_wrapped`](#method.elapsed_secs_wrapped).304#[inline]305pub fn elapsed_secs(&self) -> f32 {306self.elapsed_secs307}308309/// Returns how much time has advanced since [`startup`](#method.startup), as [`f64`] seconds.310#[inline]311pub fn elapsed_secs_f64(&self) -> f64 {312self.elapsed_secs_f64313}314315/// Returns how much time has advanced since [`startup`](#method.startup) modulo316/// the [`wrap_period`](#method.wrap_period), as [`Duration`].317#[inline]318pub fn elapsed_wrapped(&self) -> Duration {319self.elapsed_wrapped320}321322/// Returns how much time has advanced since [`startup`](#method.startup) modulo323/// the [`wrap_period`](#method.wrap_period), as [`f32`] seconds.324///325/// This method is intended for applications (e.g. shaders) that require an [`f32`] value but326/// suffer from the gradual precision loss of [`elapsed_secs`](#method.elapsed_secs).327#[inline]328pub fn elapsed_secs_wrapped(&self) -> f32 {329self.elapsed_secs_wrapped330}331332/// Returns how much time has advanced since [`startup`](#method.startup) modulo333/// the [`wrap_period`](#method.wrap_period), as [`f64`] seconds.334#[inline]335pub fn elapsed_secs_wrapped_f64(&self) -> f64 {336self.elapsed_secs_wrapped_f64337}338339/// Returns a reference to the context of this specific clock.340#[inline]341pub fn context(&self) -> &T {342&self.context343}344345/// Returns a mutable reference to the context of this specific clock.346#[inline]347pub fn context_mut(&mut self) -> &mut T {348&mut self.context349}350351/// Returns a copy of this clock as fully generic clock without context.352#[inline]353pub fn as_generic(&self) -> Time<()> {354Time {355context: (),356wrap_period: self.wrap_period,357delta: self.delta,358delta_secs: self.delta_secs,359delta_secs_f64: self.delta_secs_f64,360elapsed: self.elapsed,361elapsed_secs: self.elapsed_secs,362elapsed_secs_f64: self.elapsed_secs_f64,363elapsed_wrapped: self.elapsed_wrapped,364elapsed_secs_wrapped: self.elapsed_secs_wrapped,365elapsed_secs_wrapped_f64: self.elapsed_secs_wrapped_f64,366}367}368}369370impl<T: Default> Default for Time<T> {371fn default() -> Self {372Self {373context: Default::default(),374wrap_period: Self::DEFAULT_WRAP_PERIOD,375delta: Duration::ZERO,376delta_secs: 0.0,377delta_secs_f64: 0.0,378elapsed: Duration::ZERO,379elapsed_secs: 0.0,380elapsed_secs_f64: 0.0,381elapsed_wrapped: Duration::ZERO,382elapsed_secs_wrapped: 0.0,383elapsed_secs_wrapped_f64: 0.0,384}385}386}387388fn duration_rem(dividend: Duration, divisor: Duration) -> Duration {389// `Duration` does not have a built-in modulo operation390let quotient = (dividend.as_nanos() / divisor.as_nanos()) as u32;391dividend - (quotient * divisor)392}393394#[cfg(test)]395mod test {396use super::*;397398#[test]399fn test_initial_state() {400let time: Time = Time::default();401402assert_eq!(time.wrap_period(), Time::<()>::DEFAULT_WRAP_PERIOD);403assert_eq!(time.delta(), Duration::ZERO);404assert_eq!(time.delta_secs(), 0.0);405assert_eq!(time.delta_secs_f64(), 0.0);406assert_eq!(time.elapsed(), Duration::ZERO);407assert_eq!(time.elapsed_secs(), 0.0);408assert_eq!(time.elapsed_secs_f64(), 0.0);409assert_eq!(time.elapsed_wrapped(), Duration::ZERO);410assert_eq!(time.elapsed_secs_wrapped(), 0.0);411assert_eq!(time.elapsed_secs_wrapped_f64(), 0.0);412}413414#[test]415fn test_advance_by() {416let mut time: Time = Time::default();417418time.advance_by(Duration::from_millis(250));419420assert_eq!(time.delta(), Duration::from_millis(250));421assert_eq!(time.delta_secs(), 0.25);422assert_eq!(time.delta_secs_f64(), 0.25);423assert_eq!(time.elapsed(), Duration::from_millis(250));424assert_eq!(time.elapsed_secs(), 0.25);425assert_eq!(time.elapsed_secs_f64(), 0.25);426427time.advance_by(Duration::from_millis(500));428429assert_eq!(time.delta(), Duration::from_millis(500));430assert_eq!(time.delta_secs(), 0.5);431assert_eq!(time.delta_secs_f64(), 0.5);432assert_eq!(time.elapsed(), Duration::from_millis(750));433assert_eq!(time.elapsed_secs(), 0.75);434assert_eq!(time.elapsed_secs_f64(), 0.75);435436time.advance_by(Duration::ZERO);437438assert_eq!(time.delta(), Duration::ZERO);439assert_eq!(time.delta_secs(), 0.0);440assert_eq!(time.delta_secs_f64(), 0.0);441assert_eq!(time.elapsed(), Duration::from_millis(750));442assert_eq!(time.elapsed_secs(), 0.75);443assert_eq!(time.elapsed_secs_f64(), 0.75);444}445446#[test]447fn test_advance_to() {448let mut time: Time = Time::default();449450time.advance_to(Duration::from_millis(250));451452assert_eq!(time.delta(), Duration::from_millis(250));453assert_eq!(time.delta_secs(), 0.25);454assert_eq!(time.delta_secs_f64(), 0.25);455assert_eq!(time.elapsed(), Duration::from_millis(250));456assert_eq!(time.elapsed_secs(), 0.25);457assert_eq!(time.elapsed_secs_f64(), 0.25);458459time.advance_to(Duration::from_millis(750));460461assert_eq!(time.delta(), Duration::from_millis(500));462assert_eq!(time.delta_secs(), 0.5);463assert_eq!(time.delta_secs_f64(), 0.5);464assert_eq!(time.elapsed(), Duration::from_millis(750));465assert_eq!(time.elapsed_secs(), 0.75);466assert_eq!(time.elapsed_secs_f64(), 0.75);467468time.advance_to(Duration::from_millis(750));469470assert_eq!(time.delta(), Duration::ZERO);471assert_eq!(time.delta_secs(), 0.0);472assert_eq!(time.delta_secs_f64(), 0.0);473assert_eq!(time.elapsed(), Duration::from_millis(750));474assert_eq!(time.elapsed_secs(), 0.75);475assert_eq!(time.elapsed_secs_f64(), 0.75);476}477478#[test]479#[should_panic]480fn test_advance_to_backwards_panics() {481let mut time: Time = Time::default();482483time.advance_to(Duration::from_millis(750));484485time.advance_to(Duration::from_millis(250));486}487488#[test]489fn test_wrapping() {490let mut time: Time = Time::default();491time.set_wrap_period(Duration::from_secs(3));492493time.advance_by(Duration::from_secs(2));494495assert_eq!(time.elapsed_wrapped(), Duration::from_secs(2));496assert_eq!(time.elapsed_secs_wrapped(), 2.0);497assert_eq!(time.elapsed_secs_wrapped_f64(), 2.0);498499time.advance_by(Duration::from_secs(2));500501assert_eq!(time.elapsed_wrapped(), Duration::from_secs(1));502assert_eq!(time.elapsed_secs_wrapped(), 1.0);503assert_eq!(time.elapsed_secs_wrapped_f64(), 1.0);504505time.advance_by(Duration::from_secs(2));506507assert_eq!(time.elapsed_wrapped(), Duration::ZERO);508assert_eq!(time.elapsed_secs_wrapped(), 0.0);509assert_eq!(time.elapsed_secs_wrapped_f64(), 0.0);510511time.advance_by(Duration::new(3, 250_000_000));512513assert_eq!(time.elapsed_wrapped(), Duration::from_millis(250));514assert_eq!(time.elapsed_secs_wrapped(), 0.25);515assert_eq!(time.elapsed_secs_wrapped_f64(), 0.25);516}517518#[test]519fn test_wrapping_change() {520let mut time: Time = Time::default();521time.set_wrap_period(Duration::from_secs(5));522523time.advance_by(Duration::from_secs(8));524525assert_eq!(time.elapsed_wrapped(), Duration::from_secs(3));526assert_eq!(time.elapsed_secs_wrapped(), 3.0);527assert_eq!(time.elapsed_secs_wrapped_f64(), 3.0);528529time.set_wrap_period(Duration::from_secs(2));530531assert_eq!(time.elapsed_wrapped(), Duration::from_secs(3));532assert_eq!(time.elapsed_secs_wrapped(), 3.0);533assert_eq!(time.elapsed_secs_wrapped_f64(), 3.0);534535time.advance_by(Duration::ZERO);536537// Time will wrap to modulo duration from full `elapsed()`, not to what538// is left in `elapsed_wrapped()`. This test of values is here to ensure539// that we notice if we change that behavior.540assert_eq!(time.elapsed_wrapped(), Duration::from_secs(0));541assert_eq!(time.elapsed_secs_wrapped(), 0.0);542assert_eq!(time.elapsed_secs_wrapped_f64(), 0.0);543}544}545546547