♻️ Timer refactor to duration. Add Stopwatch struct. (#1151)

This pull request is following the discussion on the issue #1127. Additionally, it integrates the change proposed by #1112.

The list of change of this pull request:

*  Add `Timer::times_finished` method that counts the number of wraps for repeating timers.
* ♻️ Refactored `Timer`
* 🐛 Fix a bug where 2 successive calls to `Timer::tick` which makes a repeating timer to finish makes `Timer::just_finished` to return `false` where it should return `true`. Minimal failing example:
```rust
use bevy::prelude::*;
let mut timer: Timer<()> = Timer::from_seconds(1.0, true);
timer.tick(1.5);
assert!(timer.finished());
assert!(timer.just_finished());
timer.tick(1.5);
assert!(timer.finished());
assert!(timer.just_finished()); // <- This fails where it should not
```
* 📚 Add extensive documentation for Timer with doc examples.
*  Add a `Stopwatch` struct similar to `Timer` with extensive doc and tests.

Even if the type specialization is not retained for bevy, the doc, bugfix and added method are worth salvaging 😅.
This is my first PR for bevy, please be kind to me ❤️ .

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
Chris Janaqi 2021-03-05 19:59:14 +00:00
parent 4686437d7a
commit ab407aa697
11 changed files with 530 additions and 135 deletions

View file

@ -1,8 +1,10 @@
mod fixed_timestep; mod fixed_timestep;
mod stopwatch;
#[allow(clippy::module_inception)] #[allow(clippy::module_inception)]
mod time; mod time;
mod timer; mod timer;
pub use fixed_timestep::*; pub use fixed_timestep::*;
pub use stopwatch::*;
pub use time::*; pub use time::*;
pub use timer::*; pub use timer::*;

View file

@ -0,0 +1,168 @@
use bevy_ecs::reflect::ReflectComponent;
use bevy_reflect::Reflect;
use bevy_utils::Duration;
/// A Stopwatch is a struct that track elapsed time when started.
///
/// # Examples
///
/// ```
/// # use bevy_core::*;
/// use std::time::Duration;
/// let mut stopwatch = Stopwatch::new();
/// assert_eq!(stopwatch.elapsed_secs(), 0.0);
/// stopwatch.tick(Duration::from_secs_f32(1.0)); // tick one second
/// assert_eq!(stopwatch.elapsed_secs(), 1.0);
/// stopwatch.pause();
/// stopwatch.tick(Duration::from_secs_f32(1.0)); // paused stopwatches don't tick
/// assert_eq!(stopwatch.elapsed_secs(), 1.0);
/// stopwatch.reset(); // reset the stopwatch
/// assert!(stopwatch.paused());
/// assert_eq!(stopwatch.elapsed_secs(), 0.0);
/// ```
#[derive(Clone, Debug, Default, Reflect)]
#[reflect(Component)]
pub struct Stopwatch {
elapsed: Duration,
paused: bool,
}
impl Stopwatch {
/// Create a new unpaused `Stopwatch` with no elapsed time.
///
/// # Examples
/// ```
/// # use bevy_core::*;
/// let stopwatch = Stopwatch::new();
/// assert_eq!(stopwatch.elapsed_secs(), 0.0);
/// assert_eq!(stopwatch.paused(), false);
/// ```
pub fn new() -> Self {
Default::default()
}
/// Returns the elapsed time since the last [`reset`](Stopwatch::reset)
/// of the stopwatch.
///
/// # Examples
/// ```
/// # use bevy_core::*;
/// use std::time::Duration;
/// let mut stopwatch = Stopwatch::new();
/// stopwatch.tick(Duration::from_secs(1));
/// assert_eq!(stopwatch.elapsed(), Duration::from_secs(1));
/// ```
#[inline]
pub fn elapsed(&self) -> Duration {
self.elapsed
}
#[inline]
pub fn elapsed_secs(&self) -> f32 {
self.elapsed().as_secs_f32()
}
/// Sets the elapsed time of the stopwatch.
///
/// # Examples
/// ```
/// # use bevy_core::*;
/// use std::time::Duration;
/// let mut stopwatch = Stopwatch::new();
/// stopwatch.set_elapsed(Duration::from_secs_f32(1.0));
/// assert_eq!(stopwatch.elapsed_secs(), 1.0);
/// ```
#[inline]
pub fn set_elapsed(&mut self, time: Duration) {
self.elapsed = time;
}
/// Advance the stopwatch by `delta` seconds.
/// If the stopwatch is paused, ticking will not have any effect
/// on elapsed time.
///
/// # Examples
/// ```
/// # use bevy_core::*;
/// use std::time::Duration;
/// let mut stopwatch = Stopwatch::new();
/// stopwatch.tick(Duration::from_secs_f32(1.5));
/// assert_eq!(stopwatch.elapsed_secs(), 1.5);
/// ```
pub fn tick(&mut self, delta: Duration) -> &Self {
if !self.paused() {
self.elapsed += delta;
}
self
}
/// Pauses the stopwatch. Any call to [`tick`](Stopwatch::tick) while
/// paused will not have any effect on the elapsed time.
///
/// # Examples
/// ```
/// # use bevy_core::*;
/// use std::time::Duration;
/// let mut stopwatch = Stopwatch::new();
/// stopwatch.pause();
/// stopwatch.tick(Duration::from_secs_f32(1.5));
/// assert!(stopwatch.paused());
/// assert_eq!(stopwatch.elapsed_secs(), 0.0);
/// ```
#[inline]
pub fn pause(&mut self) {
self.paused = true;
}
/// Unpauses the stopwatch. Resume the effect of ticking on elapsed time.
///
/// # Examples
/// ```
/// # use bevy_core::*;
/// use std::time::Duration;
/// let mut stopwatch = Stopwatch::new();
/// stopwatch.pause();
/// stopwatch.tick(Duration::from_secs_f32(1.0));
/// stopwatch.unpause();
/// stopwatch.tick(Duration::from_secs_f32(1.0));
/// assert!(!stopwatch.paused());
/// assert_eq!(stopwatch.elapsed_secs(), 1.0);
/// ```
#[inline]
pub fn unpause(&mut self) {
self.paused = false;
}
/// Returns `true` if the stopwatch is paused.
///
/// # Examples
/// ```
/// # use bevy_core::*;
/// let mut stopwatch = Stopwatch::new();
/// assert!(!stopwatch.paused());
/// stopwatch.pause();
/// assert!(stopwatch.paused());
/// stopwatch.unpause();
/// assert!(!stopwatch.paused());
/// ```
#[inline]
pub fn paused(&self) -> bool {
self.paused
}
/// Resets the stopwatch.
///
/// # Examples
/// ```
/// # use bevy_core::*;
/// use std::time::Duration;
/// let mut stopwatch = Stopwatch::new();
/// stopwatch.tick(Duration::from_secs_f32(1.5));
/// stopwatch.reset();
/// assert_eq!(stopwatch.elapsed_secs(), 0.0);
/// ```
#[inline]
pub fn reset(&mut self) {
self.elapsed = Default::default();
}
}

View file

@ -1,3 +1,4 @@
use crate::Stopwatch;
use bevy_ecs::reflect::ReflectComponent; use bevy_ecs::reflect::ReflectComponent;
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use bevy_utils::Duration; use bevy_utils::Duration;
@ -11,228 +12,455 @@ use bevy_utils::Duration;
#[derive(Clone, Debug, Default, Reflect)] #[derive(Clone, Debug, Default, Reflect)]
#[reflect(Component)] #[reflect(Component)]
pub struct Timer { pub struct Timer {
elapsed: f32, stopwatch: Stopwatch,
duration: f32, duration: Duration,
finished: bool,
/// Will only be non-zero on the tick `duration` is reached or exceeded.
just_finished_count: u32,
paused: bool,
repeating: bool, repeating: bool,
finished: bool,
times_finished: u32,
} }
impl Timer { impl Timer {
pub fn new(duration: Duration, repeating: bool) -> Self { /// Creates a new timer with a given duration.
Timer {
duration: duration.as_secs_f32(),
repeating,
..Default::default()
}
}
pub fn from_seconds(seconds: f32, repeating: bool) -> Self {
Timer {
duration: seconds,
repeating,
..Default::default()
}
}
#[inline]
pub fn pause(&mut self) {
self.paused = true
}
#[inline]
pub fn unpause(&mut self) {
self.paused = false
}
#[inline]
pub fn paused(&self) -> bool {
self.paused
}
/// Returns the time elapsed on the timer. Guaranteed to be between 0.0 and `duration`.
/// Will only equal `duration` when the timer is finished and non repeating.
#[inline]
pub fn elapsed(&self) -> f32 {
self.elapsed
}
#[inline]
pub fn set_elapsed(&mut self, elapsed: f32) {
self.elapsed = elapsed
}
#[inline]
pub fn duration(&self) -> f32 {
self.duration
}
#[inline]
pub fn set_duration(&mut self, duration: f32) {
self.duration = duration
}
/// Returns the finished state of the timer.
/// ///
/// Non-repeating timers will stop tracking and stay in the finished state until reset. /// See also [`Timer::from_seconds`](Timer::from_seconds).
/// Repeating timers will only be in the finished state on each tick `duration` is reached or exceeded, so in that case pub fn new(duration: Duration, repeating: bool) -> Self {
/// this function is equivalent to `just_finished`. Self {
duration,
repeating,
..Default::default()
}
}
/// Creates a new timer with a given duration in seconds.
///
/// # Example
/// ```
/// # use bevy_core::*;
/// let mut timer = Timer::from_seconds(1.0, false);
/// ```
pub fn from_seconds(duration: f32, repeating: bool) -> Self {
Self {
duration: Duration::from_secs_f32(duration),
repeating,
..Default::default()
}
}
/// Returns `true` if the timer has reached its duration.
///
/// # Examples
/// ```
/// # use bevy_core::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, false);
/// timer.tick(Duration::from_secs_f32(1.5));
/// assert!(timer.finished());
/// timer.tick(Duration::from_secs_f32(0.5));
/// assert!(timer.finished());
/// ```
#[inline] #[inline]
pub fn finished(&self) -> bool { pub fn finished(&self) -> bool {
self.finished self.finished
} }
/// Will only be true on the tick the timer's duration is reached or exceeded. /// Returns `true` only on the tick the timer reached its duration.
///
/// # Examples
/// ```
/// # use bevy_core::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, false);
/// timer.tick(Duration::from_secs_f32(1.5));
/// assert!(timer.just_finished());
/// timer.tick(Duration::from_secs_f32(0.5));
/// assert!(!timer.just_finished());
/// ```
#[inline] #[inline]
pub fn just_finished(&self) -> bool { pub fn just_finished(&self) -> bool {
self.just_finished_count > 0 self.times_finished > 0
} }
/// Returns the total number of times the timer finished during this tick. /// Returns the time elapsed on the timer. Guaranteed to be between 0.0 and `duration`.
/// Will only equal `duration` when the timer is finished and non repeating.
/// ///
/// This value can be used to ensure no completions of a repeating timer are skipped over due to a tick with an unexpectedly /// See also [`Stopwatch::elapsed`](Stopwatch::elapsed).
/// long delta time. For non repeating timers, the value will only ever be 0 or 1. ///
/// # Examples
/// ```
/// # use bevy_core::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, false);
/// timer.tick(Duration::from_secs_f32(0.5));
/// assert_eq!(timer.elapsed(), Duration::from_secs_f32(0.5));
/// ```
#[inline] #[inline]
pub fn just_finished_count(&self) -> u32 { pub fn elapsed(&self) -> Duration {
self.just_finished_count self.stopwatch.elapsed()
} }
/// Returns the time elapsed on the timer as a `f32`.
/// See also [`Timer::elapsed`](Timer::elapsed).
#[inline]
pub fn elapsed_secs(&self) -> f32 {
self.stopwatch.elapsed_secs()
}
/// Sets the elapsed time of the timer without any other considerations.
///
/// See also [`Stopwatch::set`](Stopwatch::set).
///
/// #
/// ```
/// # use bevy_core::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, false);
/// timer.set_elapsed(Duration::from_secs(2));
/// assert_eq!(timer.elapsed(), Duration::from_secs(2));
/// // the timer is not finished even if the elapsed time is greater than the duration.
/// assert!(!timer.finished());
/// ```
#[inline]
pub fn set_elapsed(&mut self, time: Duration) {
self.stopwatch.set_elapsed(time);
}
/// Returns the duration of the timer.
///
/// # Examples
/// ```
/// # use bevy_core::*;
/// use std::time::Duration;
/// let timer = Timer::new(Duration::from_secs(1), false);
/// assert_eq!(timer.duration(), Duration::from_secs(1));
/// ```
#[inline]
pub fn duration(&self) -> Duration {
self.duration
}
/// Sets the duration of the timer.
///
/// # Examples
/// ```
/// # use bevy_core::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.5, false);
/// timer.set_duration(Duration::from_secs(1));
/// assert_eq!(timer.duration(), Duration::from_secs(1));
/// ```
#[inline]
pub fn set_duration(&mut self, duration: Duration) {
self.duration = duration;
}
/// Returns `true` if the timer is repeating.
///
/// # Examples
/// ```
/// # use bevy_core::*;
/// let mut timer = Timer::from_seconds(1.0, true);
/// assert!(timer.repeating());
/// ```
#[inline] #[inline]
pub fn repeating(&self) -> bool { pub fn repeating(&self) -> bool {
self.repeating self.repeating
} }
/// Sets whether the timer is repeating or not.
///
/// # Examples
/// ```
/// # use bevy_core::*;
/// let mut timer = Timer::from_seconds(1.0, true);
/// timer.set_repeating(false);
/// assert!(!timer.repeating());
/// ```
#[inline]
pub fn set_repeating(&mut self, repeating: bool) { pub fn set_repeating(&mut self, repeating: bool) {
if !self.repeating && repeating && self.finished { if !self.repeating && repeating && self.finished {
self.elapsed = 0.0; self.stopwatch.reset();
self.finished = self.just_finished(); self.finished = self.just_finished();
} }
self.repeating = repeating self.repeating = repeating
} }
/// Advances the timer by `delta` seconds. /// Advance the timer by `delta` seconds.
pub fn tick(&mut self, delta: f32) -> &Self { /// Non repeating timer will clamp at duration.
if self.paused { /// Repeating timer will wrap around.
///
/// See also [`Stopwatch::tick`](Stopwatch::tick).
///
/// # Examples
/// ```
/// # use bevy_core::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, false);
/// let mut repeating = Timer::from_seconds(1.0, true);
/// timer.tick(Duration::from_secs_f32(1.5));
/// repeating.tick(Duration::from_secs_f32(1.5));
/// assert_eq!(timer.elapsed_secs(), 1.0);
/// assert_eq!(repeating.elapsed_secs(), 0.5);
/// ```
pub fn tick(&mut self, delta: Duration) -> &Self {
if self.paused() {
return self; return self;
} }
let prev_finished = self.finished; if !self.repeating() && self.finished() {
self.elapsed += delta; self.times_finished = 0;
self.finished = self.elapsed >= self.duration; return self;
}
if self.finished { self.stopwatch.tick(delta);
if self.repeating { self.finished = self.elapsed() >= self.duration();
// Count the number of times the timer will wrap around from this tick
self.just_finished_count = (self.elapsed / self.duration) as u32; if self.finished() {
// Repeating timers wrap around if self.repeating() {
self.elapsed %= self.duration; self.times_finished = self.percent().floor() as u32;
// Duration does not have a modulo
self.set_elapsed(self.elapsed() - self.duration() * self.times_finished);
} else { } else {
self.just_finished_count = if prev_finished { 0 } else { 1 }; self.times_finished = 1;
// Non-repeating timers clamp to duration self.set_elapsed(self.duration());
self.elapsed = self.duration;
} }
} else { } else {
self.just_finished_count = 0; self.times_finished = 0;
} }
self self
} }
/// Pauses the Timer. Disables the ticking of the timer.
///
/// See also [`Stopwatch::pause`](Stopwatch::pause).
///
/// # Examples
/// ```
/// # use bevy_core::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, false);
/// timer.pause();
/// timer.tick(Duration::from_secs_f32(0.5));
/// assert_eq!(timer.elapsed_secs(), 0.0);
/// ```
#[inline] #[inline]
pub fn pause(&mut self) {
self.stopwatch.pause();
}
/// Unpauses the Timer. Resumes the ticking of the timer.
///
/// See also [`Stopwatch::unpause()`](Stopwatch::unpause).
///
/// # Examples
/// ```
/// # use bevy_core::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, false);
/// timer.pause();
/// timer.tick(Duration::from_secs_f32(0.5));
/// timer.unpause();
/// timer.tick(Duration::from_secs_f32(0.5));
/// assert_eq!(timer.elapsed_secs(), 0.5);
/// ```
#[inline]
pub fn unpause(&mut self) {
self.stopwatch.unpause();
}
/// Returns `true` if the timer is paused.
///
/// See also [`Stopwatch::paused`](Stopwatch::paused).
///
/// # Examples
/// ```
/// # use bevy_core::*;
/// let mut timer = Timer::from_seconds(1.0, false);
/// assert!(!timer.paused());
/// timer.pause();
/// assert!(timer.paused());
/// timer.unpause();
/// assert!(!timer.paused());
/// ```
#[inline]
pub fn paused(&self) -> bool {
self.stopwatch.paused()
}
/// Resets the timer. the reset doesn't affect the `paused` state of the timer.
///
/// See also [`Stopwatch::reset`](Stopwatch::reset).
///
/// Examples
/// ```
/// # use bevy_core::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, false);
/// timer.tick(Duration::from_secs_f32(1.5));
/// timer.reset();
/// assert!(!timer.finished());
/// assert!(!timer.just_finished());
/// assert_eq!(timer.elapsed_secs(), 0.0);
/// ```
pub fn reset(&mut self) { pub fn reset(&mut self) {
self.stopwatch.reset();
self.finished = false; self.finished = false;
self.just_finished_count = 0; self.times_finished = 0;
self.elapsed = 0.0;
} }
/// Percent timer has elapsed (goes from 0.0 to 1.0) /// Returns the percentage of the timer elapsed time (goes from 0.0 to 1.0).
///
/// # Examples
/// ```
/// # use bevy_core::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(2.0, false);
/// timer.tick(Duration::from_secs_f32(0.5));
/// assert_eq!(timer.percent(), 0.25);
/// ```
#[inline]
pub fn percent(&self) -> f32 { pub fn percent(&self) -> f32 {
self.elapsed / self.duration self.elapsed().as_secs_f32() / self.duration().as_secs_f32()
} }
/// Percent left on timer (goes from 1.0 to 0.0) /// Returns the percentage of the timer remaining time (goes from 0.0 to 1.0).
///
/// # Examples
/// ```
/// # use bevy_core::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(2.0, false);
/// timer.tick(Duration::from_secs_f32(0.5));
/// assert_eq!(timer.percent_left(), 0.75);
/// ```
#[inline]
pub fn percent_left(&self) -> f32 { pub fn percent_left(&self) -> f32 {
(self.duration - self.elapsed) / self.duration 1.0 - self.percent()
}
/// Returns the number of times a repeating timer
/// finished during the last [`tick`](Timer<T>::tick) call.
///
/// For non repeating-timers, this method will only ever
/// return 0 or 1.
///
/// # Examples
/// ```
/// # use bevy_core::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, true);
/// timer.tick(Duration::from_secs_f32(6.0));
/// assert_eq!(timer.times_finished(), 6);
/// timer.tick(Duration::from_secs_f32(2.0));
/// assert_eq!(timer.times_finished(), 2);
/// timer.tick(Duration::from_secs_f32(0.5));
/// assert_eq!(timer.times_finished(), 0);
/// ```
#[inline]
pub fn times_finished(&self) -> u32 {
self.times_finished
} }
} }
#[cfg(test)] #[cfg(test)]
#[allow(clippy::float_cmp)] #[allow(clippy::float_cmp)]
mod tests { mod tests {
use super::Timer; use super::*;
#[test] #[test]
fn test_non_repeating() { fn non_repeating_timer() {
let mut t = Timer::from_seconds(10.0, false); let mut t = Timer::from_seconds(10.0, false);
// Tick once, check all attributes // Tick once, check all attributes
t.tick(0.25); t.tick(Duration::from_secs_f32(0.25));
assert_eq!(t.elapsed(), 0.25); assert_eq!(t.elapsed_secs(), 0.25);
assert_eq!(t.duration(), 10.0); assert_eq!(t.duration(), Duration::from_secs_f32(10.0));
assert_eq!(t.finished(), false); assert_eq!(t.finished(), false);
assert_eq!(t.just_finished(), false); assert_eq!(t.just_finished(), false);
assert_eq!(t.just_finished_count(), 0); assert_eq!(t.times_finished(), 0);
assert_eq!(t.repeating(), false); assert_eq!(t.repeating(), false);
assert_eq!(t.percent(), 0.025); assert_eq!(t.percent(), 0.025);
assert_eq!(t.percent_left(), 0.975); assert_eq!(t.percent_left(), 0.975);
// Ticking while paused changes nothing // Ticking while paused changes nothing
t.pause(); t.pause();
t.tick(500.0); t.tick(Duration::from_secs_f32(500.0));
assert_eq!(t.elapsed(), 0.25); assert_eq!(t.elapsed_secs(), 0.25);
assert_eq!(t.duration(), 10.0); assert_eq!(t.duration(), Duration::from_secs_f32(10.0));
assert_eq!(t.finished(), false); assert_eq!(t.finished(), false);
assert_eq!(t.just_finished(), false); assert_eq!(t.just_finished(), false);
assert_eq!(t.just_finished_count(), 0); assert_eq!(t.times_finished(), 0);
assert_eq!(t.repeating(), false); assert_eq!(t.repeating(), false);
assert_eq!(t.percent(), 0.025); assert_eq!(t.percent(), 0.025);
assert_eq!(t.percent_left(), 0.975); assert_eq!(t.percent_left(), 0.975);
// Tick past the end and make sure elapsed doesn't go past 0.0 and other things update // Tick past the end and make sure elapsed doesn't go past 0.0 and other things update
t.unpause(); t.unpause();
t.tick(500.0); t.tick(Duration::from_secs_f32(500.0));
assert_eq!(t.elapsed(), 10.0); assert_eq!(t.elapsed_secs(), 10.0);
assert_eq!(t.finished(), true); assert_eq!(t.finished(), true);
assert_eq!(t.just_finished(), true); assert_eq!(t.just_finished(), true);
assert_eq!(t.just_finished_count(), 1); assert_eq!(t.times_finished(), 1);
assert_eq!(t.percent(), 1.0); assert_eq!(t.percent(), 1.0);
assert_eq!(t.percent_left(), 0.0); assert_eq!(t.percent_left(), 0.0);
// Continuing to tick when finished should only change just_finished // Continuing to tick when finished should only change just_finished
t.tick(1.0); t.tick(Duration::from_secs_f32(1.0));
assert_eq!(t.elapsed(), 10.0); assert_eq!(t.elapsed_secs(), 10.0);
assert_eq!(t.finished(), true); assert_eq!(t.finished(), true);
assert_eq!(t.just_finished(), false); assert_eq!(t.just_finished(), false);
assert_eq!(t.just_finished_count(), 0); assert_eq!(t.times_finished(), 0);
assert_eq!(t.percent(), 1.0); assert_eq!(t.percent(), 1.0);
assert_eq!(t.percent_left(), 0.0); assert_eq!(t.percent_left(), 0.0);
} }
#[test] #[test]
fn test_repeating() { fn repeating_timer() {
let mut t = Timer::from_seconds(2.0, true); let mut t = Timer::from_seconds(2.0, true);
// Tick once, check all attributes // Tick once, check all attributes
t.tick(0.75); t.tick(Duration::from_secs_f32(0.75));
assert_eq!(t.elapsed(), 0.75); assert_eq!(t.elapsed_secs(), 0.75);
assert_eq!(t.duration(), 2.0); assert_eq!(t.duration(), Duration::from_secs_f32(2.0));
assert_eq!(t.finished(), false); assert_eq!(t.finished(), false);
assert_eq!(t.just_finished(), false); assert_eq!(t.just_finished(), false);
assert_eq!(t.just_finished_count(), 0); assert_eq!(t.times_finished(), 0);
assert_eq!(t.repeating(), true); assert_eq!(t.repeating(), true);
assert_eq!(t.percent(), 0.375); assert_eq!(t.percent(), 0.375);
assert_eq!(t.percent_left(), 0.625); assert_eq!(t.percent_left(), 0.625);
// Tick past the end and make sure elapsed wraps // Tick past the end and make sure elapsed wraps
t.tick(3.5); t.tick(Duration::from_secs_f32(1.5));
assert_eq!(t.elapsed(), 0.25); assert_eq!(t.elapsed_secs(), 0.25);
assert_eq!(t.finished(), true); assert_eq!(t.finished(), true);
assert_eq!(t.just_finished(), true); assert_eq!(t.just_finished(), true);
assert_eq!(t.just_finished_count(), 2); assert_eq!(t.times_finished(), 1);
assert_eq!(t.percent(), 0.125); assert_eq!(t.percent(), 0.125);
assert_eq!(t.percent_left(), 0.875); assert_eq!(t.percent_left(), 0.875);
// Continuing to tick should turn off both finished & just_finished for repeating timers // Continuing to tick should turn off both finished & just_finished for repeating timers
t.tick(1.0); t.tick(Duration::from_secs_f32(1.0));
assert_eq!(t.elapsed(), 1.25); assert_eq!(t.elapsed_secs(), 1.25);
assert_eq!(t.finished(), false); assert_eq!(t.finished(), false);
assert_eq!(t.just_finished(), false); assert_eq!(t.just_finished(), false);
assert_eq!(t.just_finished_count(), 0); assert_eq!(t.times_finished(), 0);
assert_eq!(t.percent(), 0.625); assert_eq!(t.percent(), 0.625);
assert_eq!(t.percent_left(), 0.375); assert_eq!(t.percent_left(), 0.375);
} }
#[test]
fn times_finished_repeating() {
let mut t = Timer::from_seconds(1.0, true);
assert_eq!(t.times_finished(), 0);
t.tick(Duration::from_secs_f32(3.5));
assert_eq!(t.times_finished(), 3);
assert_eq!(t.elapsed_secs(), 0.5);
assert!(t.finished());
assert!(t.just_finished());
t.tick(Duration::from_secs_f32(0.2));
assert_eq!(t.times_finished(), 0);
}
#[test]
fn times_finished() {
let mut t = Timer::from_seconds(1.0, false);
assert_eq!(t.times_finished(), 0);
t.tick(Duration::from_secs_f32(1.5));
assert_eq!(t.times_finished(), 1);
t.tick(Duration::from_secs_f32(0.5));
assert_eq!(t.times_finished(), 0);
}
} }

View file

@ -90,7 +90,7 @@ impl LogDiagnosticsPlugin {
time: Res<Time>, time: Res<Time>,
diagnostics: Res<Diagnostics>, diagnostics: Res<Diagnostics>,
) { ) {
if state.timer.tick(time.delta_seconds()).finished() { if state.timer.tick(time.delta()).finished() {
if let Some(ref filter) = state.filter { if let Some(ref filter) = state.filter {
for diagnostic in filter.iter().map(|id| diagnostics.get(*id).unwrap()) { for diagnostic in filter.iter().map(|id| diagnostics.get(*id).unwrap()) {
Self::log_diagnostic(diagnostic); Self::log_diagnostic(diagnostic);
@ -108,7 +108,7 @@ impl LogDiagnosticsPlugin {
time: Res<Time>, time: Res<Time>,
diagnostics: Res<Diagnostics>, diagnostics: Res<Diagnostics>,
) { ) {
if state.timer.tick(time.delta_seconds()).finished() { if state.timer.tick(time.delta()).finished() {
if let Some(ref filter) = state.filter { if let Some(ref filter) = state.filter {
for diagnostic in filter.iter().map(|id| diagnostics.get(*id).unwrap()) { for diagnostic in filter.iter().map(|id| diagnostics.get(*id).unwrap()) {
debug!("{:#?}\n", diagnostic); debug!("{:#?}\n", diagnostic);

View file

@ -4,7 +4,7 @@ use crate::{
}; };
use bevy_reflect_derive::impl_reflect_value; use bevy_reflect_derive::impl_reflect_value;
use bevy_utils::{HashMap, HashSet}; use bevy_utils::{Duration, HashMap, HashSet};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
any::Any, any::Any,
@ -32,6 +32,7 @@ impl_reflect_value!(String(Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(Option<T: Serialize + Clone + for<'de> Deserialize<'de> + Reflect + 'static>(Serialize, Deserialize)); impl_reflect_value!(Option<T: Serialize + Clone + for<'de> Deserialize<'de> + Reflect + 'static>(Serialize, Deserialize));
impl_reflect_value!(HashSet<T: Serialize + Hash + Eq + Clone + for<'de> Deserialize<'de> + Send + Sync + 'static>(Serialize, Deserialize)); impl_reflect_value!(HashSet<T: Serialize + Hash + Eq + Clone + for<'de> Deserialize<'de> + Send + Sync + 'static>(Serialize, Deserialize));
impl_reflect_value!(Range<T: Serialize + Clone + for<'de> Deserialize<'de> + Send + Sync + 'static>(Serialize, Deserialize)); impl_reflect_value!(Range<T: Serialize + Clone + for<'de> Deserialize<'de> + Send + Sync + 'static>(Serialize, Deserialize));
impl_reflect_value!(Duration);
impl<T: Reflect> List for Vec<T> { impl<T: Reflect> List for Vec<T> {
fn get(&self, index: usize) -> Option<&dyn Reflect> { fn get(&self, index: usize) -> Option<&dyn Reflect> {

View file

@ -151,7 +151,7 @@ fn select_system(
) { ) {
let mut timer_fired = false; let mut timer_fired = false;
for mut t in tq.iter_mut() { for mut t in tq.iter_mut() {
if !t.tick(time.delta_seconds()).just_finished() { if !t.tick(time.delta()).just_finished() {
continue; continue;
} }
t.reset(); t.reset();

View file

@ -14,7 +14,7 @@ fn animate_sprite_system(
mut query: Query<(&mut Timer, &mut TextureAtlasSprite, &Handle<TextureAtlas>)>, mut query: Query<(&mut Timer, &mut TextureAtlasSprite, &Handle<TextureAtlas>)>,
) { ) {
for (mut timer, mut sprite, texture_atlas_handle) in query.iter_mut() { for (mut timer, mut sprite, texture_atlas_handle) in query.iter_mut() {
timer.tick(time.delta_seconds()); timer.tick(time.delta());
if timer.finished() { if timer.finished() {
let texture_atlas = texture_atlases.get(texture_atlas_handle).unwrap(); let texture_atlas = texture_atlases.get(texture_atlas_handle).unwrap();
sprite.index = ((sprite.index as usize + 1) % texture_atlas.textures.len()) as u32; sprite.index = ((sprite.index as usize + 1) % texture_atlas.textures.len()) as u32;

View file

@ -39,7 +39,7 @@ struct PrintMessageState {
} }
fn print_message_system(mut state: ResMut<PrintMessageState>, time: Res<Time>) { fn print_message_system(mut state: ResMut<PrintMessageState>, time: Res<Time>) {
if state.timer.tick(time.delta_seconds()).finished() { if state.timer.tick(time.delta()).finished() {
println!("{}", state.message); println!("{}", state.message);
} }
} }

View file

@ -34,7 +34,7 @@ fn event_trigger_system(
mut state: ResMut<EventTriggerState>, mut state: ResMut<EventTriggerState>,
mut my_events: ResMut<Events<MyEvent>>, mut my_events: ResMut<Events<MyEvent>>,
) { ) {
if state.event_timer.tick(time.delta_seconds()).finished() { if state.event_timer.tick(time.delta()).finished() {
my_events.send(MyEvent { my_events.send(MyEvent {
message: "MyEvent just happened!".to_string(), message: "MyEvent just happened!".to_string(),
}); });

View file

@ -39,7 +39,7 @@ fn setup_system(mut commands: Commands) {
/// using bevy's `Time` resource to get the delta between each update. /// using bevy's `Time` resource to get the delta between each update.
fn timer_system(time: Res<Time>, mut query: Query<&mut Timer>) { fn timer_system(time: Res<Time>, mut query: Query<&mut Timer>) {
for mut timer in query.iter_mut() { for mut timer in query.iter_mut() {
if timer.tick(time.delta_seconds()).just_finished() { if timer.tick(time.delta()).just_finished() {
info!("Entity timer just finished") info!("Entity timer just finished")
} }
} }
@ -48,16 +48,12 @@ fn timer_system(time: Res<Time>, mut query: Query<&mut Timer>) {
/// This system controls ticking the timer within the countdown resource and /// This system controls ticking the timer within the countdown resource and
/// handling its state. /// handling its state.
fn countdown_system(time: Res<Time>, mut countdown: ResMut<Countdown>) { fn countdown_system(time: Res<Time>, mut countdown: ResMut<Countdown>) {
countdown.main_timer.tick(time.delta_seconds()); countdown.main_timer.tick(time.delta());
// The API encourages this kind of timer state checking (if you're only checking for one value) // The API encourages this kind of timer state checking (if you're only checking for one value)
// Additionally, `finished()` would accomplish the same thing as `just_finished` due to the timer // Additionally, `finished()` would accomplish the same thing as `just_finished` due to the timer
// being repeating, however this makes more sense visually. // being repeating, however this makes more sense visually.
if countdown if countdown.percent_trigger.tick(time.delta()).just_finished() {
.percent_trigger
.tick(time.delta_seconds())
.just_finished()
{
if !countdown.main_timer.finished() { if !countdown.main_timer.finished() {
// Print the percent complete the main timer is. // Print the percent complete the main timer is.
info!( info!(

View file

@ -64,7 +64,7 @@ fn atlas_render_system(
} }
fn text_update_system(mut state: ResMut<State>, time: Res<Time>, mut query: Query<&mut Text>) { fn text_update_system(mut state: ResMut<State>, time: Res<Time>, mut query: Query<&mut Text>) {
if state.timer.tick(time.delta_seconds()).finished() { if state.timer.tick(time.delta()).finished() {
for mut text in query.iter_mut() { for mut text in query.iter_mut() {
let c = rand::random::<u8>() as char; let c = rand::random::<u8>() as char;
if !text.sections[0].value.contains(c) { if !text.sections[0].value.contains(c) {