mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 04:33:37 +00:00
♻️ 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:
parent
4686437d7a
commit
ab407aa697
11 changed files with 530 additions and 135 deletions
|
@ -1,8 +1,10 @@
|
|||
mod fixed_timestep;
|
||||
mod stopwatch;
|
||||
#[allow(clippy::module_inception)]
|
||||
mod time;
|
||||
mod timer;
|
||||
|
||||
pub use fixed_timestep::*;
|
||||
pub use stopwatch::*;
|
||||
pub use time::*;
|
||||
pub use timer::*;
|
||||
|
|
168
crates/bevy_core/src/time/stopwatch.rs
Normal file
168
crates/bevy_core/src/time/stopwatch.rs
Normal 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();
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
use crate::Stopwatch;
|
||||
use bevy_ecs::reflect::ReflectComponent;
|
||||
use bevy_reflect::Reflect;
|
||||
use bevy_utils::Duration;
|
||||
|
@ -11,228 +12,455 @@ use bevy_utils::Duration;
|
|||
#[derive(Clone, Debug, Default, Reflect)]
|
||||
#[reflect(Component)]
|
||||
pub struct Timer {
|
||||
elapsed: f32,
|
||||
duration: f32,
|
||||
finished: bool,
|
||||
/// Will only be non-zero on the tick `duration` is reached or exceeded.
|
||||
just_finished_count: u32,
|
||||
paused: bool,
|
||||
stopwatch: Stopwatch,
|
||||
duration: Duration,
|
||||
repeating: bool,
|
||||
finished: bool,
|
||||
times_finished: u32,
|
||||
}
|
||||
|
||||
impl Timer {
|
||||
pub fn new(duration: Duration, repeating: bool) -> Self {
|
||||
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.
|
||||
/// Creates a new timer with a given duration.
|
||||
///
|
||||
/// Non-repeating timers will stop tracking and stay in the finished state until reset.
|
||||
/// Repeating timers will only be in the finished state on each tick `duration` is reached or exceeded, so in that case
|
||||
/// this function is equivalent to `just_finished`.
|
||||
/// See also [`Timer::from_seconds`](Timer::from_seconds).
|
||||
pub fn new(duration: Duration, repeating: bool) -> Self {
|
||||
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]
|
||||
pub fn finished(&self) -> bool {
|
||||
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]
|
||||
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
|
||||
/// long delta time. For non repeating timers, the value will only ever be 0 or 1.
|
||||
/// See also [`Stopwatch::elapsed`](Stopwatch::elapsed).
|
||||
///
|
||||
/// # 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]
|
||||
pub fn just_finished_count(&self) -> u32 {
|
||||
self.just_finished_count
|
||||
pub fn elapsed(&self) -> Duration {
|
||||
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]
|
||||
pub fn repeating(&self) -> bool {
|
||||
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) {
|
||||
if !self.repeating && repeating && self.finished {
|
||||
self.elapsed = 0.0;
|
||||
self.stopwatch.reset();
|
||||
self.finished = self.just_finished();
|
||||
}
|
||||
self.repeating = repeating
|
||||
}
|
||||
|
||||
/// Advances the timer by `delta` seconds.
|
||||
pub fn tick(&mut self, delta: f32) -> &Self {
|
||||
if self.paused {
|
||||
/// Advance the timer by `delta` seconds.
|
||||
/// Non repeating timer will clamp at duration.
|
||||
/// 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;
|
||||
}
|
||||
|
||||
let prev_finished = self.finished;
|
||||
self.elapsed += delta;
|
||||
self.finished = self.elapsed >= self.duration;
|
||||
if !self.repeating() && self.finished() {
|
||||
self.times_finished = 0;
|
||||
return self;
|
||||
}
|
||||
|
||||
if self.finished {
|
||||
if self.repeating {
|
||||
// Count the number of times the timer will wrap around from this tick
|
||||
self.just_finished_count = (self.elapsed / self.duration) as u32;
|
||||
// Repeating timers wrap around
|
||||
self.elapsed %= self.duration;
|
||||
self.stopwatch.tick(delta);
|
||||
self.finished = self.elapsed() >= self.duration();
|
||||
|
||||
if self.finished() {
|
||||
if self.repeating() {
|
||||
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 {
|
||||
self.just_finished_count = if prev_finished { 0 } else { 1 };
|
||||
// Non-repeating timers clamp to duration
|
||||
self.elapsed = self.duration;
|
||||
self.times_finished = 1;
|
||||
self.set_elapsed(self.duration());
|
||||
}
|
||||
} else {
|
||||
self.just_finished_count = 0;
|
||||
self.times_finished = 0;
|
||||
}
|
||||
|
||||
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]
|
||||
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) {
|
||||
self.stopwatch.reset();
|
||||
self.finished = false;
|
||||
self.just_finished_count = 0;
|
||||
self.elapsed = 0.0;
|
||||
self.times_finished = 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 {
|
||||
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 {
|
||||
(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)]
|
||||
#[allow(clippy::float_cmp)]
|
||||
mod tests {
|
||||
use super::Timer;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_non_repeating() {
|
||||
fn non_repeating_timer() {
|
||||
let mut t = Timer::from_seconds(10.0, false);
|
||||
// Tick once, check all attributes
|
||||
t.tick(0.25);
|
||||
assert_eq!(t.elapsed(), 0.25);
|
||||
assert_eq!(t.duration(), 10.0);
|
||||
t.tick(Duration::from_secs_f32(0.25));
|
||||
assert_eq!(t.elapsed_secs(), 0.25);
|
||||
assert_eq!(t.duration(), Duration::from_secs_f32(10.0));
|
||||
assert_eq!(t.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.percent(), 0.025);
|
||||
assert_eq!(t.percent_left(), 0.975);
|
||||
// Ticking while paused changes nothing
|
||||
t.pause();
|
||||
t.tick(500.0);
|
||||
assert_eq!(t.elapsed(), 0.25);
|
||||
assert_eq!(t.duration(), 10.0);
|
||||
t.tick(Duration::from_secs_f32(500.0));
|
||||
assert_eq!(t.elapsed_secs(), 0.25);
|
||||
assert_eq!(t.duration(), Duration::from_secs_f32(10.0));
|
||||
assert_eq!(t.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.percent(), 0.025);
|
||||
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
|
||||
t.unpause();
|
||||
t.tick(500.0);
|
||||
assert_eq!(t.elapsed(), 10.0);
|
||||
t.tick(Duration::from_secs_f32(500.0));
|
||||
assert_eq!(t.elapsed_secs(), 10.0);
|
||||
assert_eq!(t.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_left(), 0.0);
|
||||
// Continuing to tick when finished should only change just_finished
|
||||
t.tick(1.0);
|
||||
assert_eq!(t.elapsed(), 10.0);
|
||||
t.tick(Duration::from_secs_f32(1.0));
|
||||
assert_eq!(t.elapsed_secs(), 10.0);
|
||||
assert_eq!(t.finished(), true);
|
||||
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_left(), 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_repeating() {
|
||||
fn repeating_timer() {
|
||||
let mut t = Timer::from_seconds(2.0, true);
|
||||
// Tick once, check all attributes
|
||||
t.tick(0.75);
|
||||
assert_eq!(t.elapsed(), 0.75);
|
||||
assert_eq!(t.duration(), 2.0);
|
||||
t.tick(Duration::from_secs_f32(0.75));
|
||||
assert_eq!(t.elapsed_secs(), 0.75);
|
||||
assert_eq!(t.duration(), Duration::from_secs_f32(2.0));
|
||||
assert_eq!(t.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.percent(), 0.375);
|
||||
assert_eq!(t.percent_left(), 0.625);
|
||||
// Tick past the end and make sure elapsed wraps
|
||||
t.tick(3.5);
|
||||
assert_eq!(t.elapsed(), 0.25);
|
||||
t.tick(Duration::from_secs_f32(1.5));
|
||||
assert_eq!(t.elapsed_secs(), 0.25);
|
||||
assert_eq!(t.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_left(), 0.875);
|
||||
// Continuing to tick should turn off both finished & just_finished for repeating timers
|
||||
t.tick(1.0);
|
||||
assert_eq!(t.elapsed(), 1.25);
|
||||
t.tick(Duration::from_secs_f32(1.0));
|
||||
assert_eq!(t.elapsed_secs(), 1.25);
|
||||
assert_eq!(t.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_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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ impl LogDiagnosticsPlugin {
|
|||
time: Res<Time>,
|
||||
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 {
|
||||
for diagnostic in filter.iter().map(|id| diagnostics.get(*id).unwrap()) {
|
||||
Self::log_diagnostic(diagnostic);
|
||||
|
@ -108,7 +108,7 @@ impl LogDiagnosticsPlugin {
|
|||
time: Res<Time>,
|
||||
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 {
|
||||
for diagnostic in filter.iter().map(|id| diagnostics.get(*id).unwrap()) {
|
||||
debug!("{:#?}\n", diagnostic);
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||
};
|
||||
|
||||
use bevy_reflect_derive::impl_reflect_value;
|
||||
use bevy_utils::{HashMap, HashSet};
|
||||
use bevy_utils::{Duration, HashMap, HashSet};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
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!(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!(Duration);
|
||||
|
||||
impl<T: Reflect> List for Vec<T> {
|
||||
fn get(&self, index: usize) -> Option<&dyn Reflect> {
|
||||
|
|
|
@ -151,7 +151,7 @@ fn select_system(
|
|||
) {
|
||||
let mut timer_fired = false;
|
||||
for mut t in tq.iter_mut() {
|
||||
if !t.tick(time.delta_seconds()).just_finished() {
|
||||
if !t.tick(time.delta()).just_finished() {
|
||||
continue;
|
||||
}
|
||||
t.reset();
|
||||
|
|
|
@ -14,7 +14,7 @@ fn animate_sprite_system(
|
|||
mut query: Query<(&mut Timer, &mut TextureAtlasSprite, &Handle<TextureAtlas>)>,
|
||||
) {
|
||||
for (mut timer, mut sprite, texture_atlas_handle) in query.iter_mut() {
|
||||
timer.tick(time.delta_seconds());
|
||||
timer.tick(time.delta());
|
||||
if timer.finished() {
|
||||
let texture_atlas = texture_atlases.get(texture_atlas_handle).unwrap();
|
||||
sprite.index = ((sprite.index as usize + 1) % texture_atlas.textures.len()) as u32;
|
||||
|
|
|
@ -39,7 +39,7 @@ struct PrintMessageState {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ fn event_trigger_system(
|
|||
mut state: ResMut<EventTriggerState>,
|
||||
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 {
|
||||
message: "MyEvent just happened!".to_string(),
|
||||
});
|
||||
|
|
|
@ -39,7 +39,7 @@ fn setup_system(mut commands: Commands) {
|
|||
/// using bevy's `Time` resource to get the delta between each update.
|
||||
fn timer_system(time: Res<Time>, mut query: Query<&mut Timer>) {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
/// handling its state.
|
||||
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)
|
||||
// Additionally, `finished()` would accomplish the same thing as `just_finished` due to the timer
|
||||
// being repeating, however this makes more sense visually.
|
||||
if countdown
|
||||
.percent_trigger
|
||||
.tick(time.delta_seconds())
|
||||
.just_finished()
|
||||
{
|
||||
if countdown.percent_trigger.tick(time.delta()).just_finished() {
|
||||
if !countdown.main_timer.finished() {
|
||||
// Print the percent complete the main timer is.
|
||||
info!(
|
||||
|
|
|
@ -64,7 +64,7 @@ fn atlas_render_system(
|
|||
}
|
||||
|
||||
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() {
|
||||
let c = rand::random::<u8>() as char;
|
||||
if !text.sections[0].value.contains(c) {
|
||||
|
|
Loading…
Reference in a new issue