Skip to content

Commit

Permalink
Replace the bool argument of Timer with TimerMode (bevyengine#6247
Browse files Browse the repository at this point in the history
)

As mentioned in bevyengine#2926, it's better to have an explicit type that clearly communicates the intent of the timer mode rather than an opaque boolean, which can be only understood when knowing the signature or having to look up the documentation.

This also opens up a way to merge different timers, such as `Stopwatch`, and possibly future ones, such as `DiscreteStopwatch` and `DiscreteTimer` from bevyengine#2683, into one struct.

Signed-off-by: Lena Milizé <me@lvmn.org>

# Objective

Fixes bevyengine#2926.

## Solution

Introduce `TimerMode` which replaces the `bool` argument of `Timer` constructors. A `Default` value for `TimerMode` is `Once`.

---

## Changelog

### Added

- `TimerMode` enum, along with variants `TimerMode::Once` and `TimerMode::Repeating`

### Changed

- Replace `bool` argument of `Timer::new` and `Timer::from_seconds` with `TimerMode`
- Change `repeating: bool` field of `Timer` with `mode: TimerMode`

## Migration Guide

- Replace `Timer::new(duration, false)` with `Timer::new(duration, TimerMode::Once)`.
- Replace `Timer::new(duration, true)` with `Timer::new(duration, TimerMode::Repeating)`.
- Replace `Timer::from_seconds(seconds, false)` with `Timer::from_seconds(seconds, TimerMode::Once)`.
- Replace `Timer::from_seconds(seconds, true)` with `Timer::from_seconds(seconds, TimerMode::Repeating)`.
- Change `timer.repeating()` to `timer.mode() == TimerMode::Repeating`.
  • Loading branch information
lovelymono authored and ItsDoot committed Feb 1, 2023
1 parent 5344ee0 commit c242655
Show file tree
Hide file tree
Showing 17 changed files with 86 additions and 69 deletions.
4 changes: 2 additions & 2 deletions crates/bevy_diagnostic/src/log_diagnostics_plugin.rs
Expand Up @@ -2,7 +2,7 @@ use super::{Diagnostic, DiagnosticId, Diagnostics};
use bevy_app::prelude::*;
use bevy_ecs::system::{Res, ResMut, Resource};
use bevy_log::{debug, info};
use bevy_time::{Time, Timer};
use bevy_time::{Time, Timer, TimerMode};
use bevy_utils::Duration;

/// An App Plugin that logs diagnostics to the console
Expand Down Expand Up @@ -32,7 +32,7 @@ impl Default for LogDiagnosticsPlugin {
impl Plugin for LogDiagnosticsPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(LogDiagnosticsState {
timer: Timer::new(self.wait_duration, true),
timer: Timer::new(self.wait_duration, TimerMode::Repeating),
filter: self.filter.clone(),
});

Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_time/src/lib.rs
Expand Up @@ -16,7 +16,7 @@ use crossbeam_channel::{Receiver, Sender};
pub mod prelude {
//! The Bevy Time Prelude.
#[doc(hidden)]
pub use crate::{Time, Timer};
pub use crate::{Time, Timer, TimerMode};
}

use bevy_app::prelude::*;
Expand Down
103 changes: 57 additions & 46 deletions crates/bevy_time/src/timer.rs
Expand Up @@ -14,7 +14,7 @@ use bevy_utils::Duration;
pub struct Timer {
stopwatch: Stopwatch,
duration: Duration,
repeating: bool,
mode: TimerMode,
finished: bool,
times_finished_this_tick: u32,
}
Expand All @@ -23,10 +23,10 @@ impl Timer {
/// Creates a new timer with a given duration.
///
/// See also [`Timer::from_seconds`](Timer::from_seconds).
pub fn new(duration: Duration, repeating: bool) -> Self {
pub fn new(duration: Duration, mode: TimerMode) -> Self {
Self {
duration,
repeating,
mode,
..Default::default()
}
}
Expand All @@ -36,12 +36,12 @@ impl Timer {
/// # Example
/// ```
/// # use bevy_time::*;
/// let mut timer = Timer::from_seconds(1.0, false);
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
/// ```
pub fn from_seconds(duration: f32, repeating: bool) -> Self {
pub fn from_seconds(duration: f32, mode: TimerMode) -> Self {
Self {
duration: Duration::from_secs_f32(duration),
repeating,
mode,
..Default::default()
}
}
Expand All @@ -52,7 +52,7 @@ impl Timer {
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, false);
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
/// timer.tick(Duration::from_secs_f32(1.5));
/// assert!(timer.finished());
/// timer.tick(Duration::from_secs_f32(0.5));
Expand All @@ -69,7 +69,7 @@ impl Timer {
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, false);
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
/// timer.tick(Duration::from_secs_f32(1.5));
/// assert!(timer.just_finished());
/// timer.tick(Duration::from_secs_f32(0.5));
Expand All @@ -89,7 +89,7 @@ impl Timer {
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, false);
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
/// timer.tick(Duration::from_secs_f32(0.5));
/// assert_eq!(timer.elapsed(), Duration::from_secs_f32(0.5));
/// ```
Expand All @@ -113,7 +113,7 @@ impl Timer {
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, false);
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
/// 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.
Expand All @@ -130,7 +130,7 @@ impl Timer {
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let timer = Timer::new(Duration::from_secs(1), false);
/// let timer = Timer::new(Duration::from_secs(1), TimerMode::Once);
/// assert_eq!(timer.duration(), Duration::from_secs(1));
/// ```
#[inline]
Expand All @@ -144,7 +144,7 @@ impl Timer {
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.5, false);
/// let mut timer = Timer::from_seconds(1.5, TimerMode::Once);
/// timer.set_duration(Duration::from_secs(1));
/// assert_eq!(timer.duration(), Duration::from_secs(1));
/// ```
Expand All @@ -158,30 +158,30 @@ impl Timer {
/// # Examples
/// ```
/// # use bevy_time::*;
/// let mut timer = Timer::from_seconds(1.0, true);
/// assert!(timer.repeating());
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Repeating);
/// assert_eq!(timer.mode(), TimerMode::Repeating);
/// ```
#[inline]
pub fn repeating(&self) -> bool {
self.repeating
pub fn mode(&self) -> TimerMode {
self.mode
}

/// Sets whether the timer is repeating or not.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// let mut timer = Timer::from_seconds(1.0, true);
/// timer.set_repeating(false);
/// assert!(!timer.repeating());
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Repeating);
/// timer.set_mode(TimerMode::Once);
/// assert_eq!(timer.mode(), TimerMode::Once);
/// ```
#[inline]
pub fn set_repeating(&mut self, repeating: bool) {
if !self.repeating && repeating && self.finished {
pub fn set_mode(&mut self, mode: TimerMode) {
if self.mode != TimerMode::Repeating && mode == TimerMode::Repeating && self.finished {
self.stopwatch.reset();
self.finished = self.just_finished();
}
self.repeating = repeating;
self.mode = mode;
}

/// Advance the timer by `delta` seconds.
Expand All @@ -194,8 +194,8 @@ impl Timer {
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, false);
/// let mut repeating = Timer::from_seconds(1.0, true);
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
/// let mut repeating = Timer::from_seconds(1.0, TimerMode::Repeating);
/// timer.tick(Duration::from_secs_f32(1.5));
/// repeating.tick(Duration::from_secs_f32(1.5));
/// assert_eq!(timer.elapsed_secs(), 1.0);
Expand All @@ -204,13 +204,13 @@ impl Timer {
pub fn tick(&mut self, delta: Duration) -> &Self {
if self.paused() {
self.times_finished_this_tick = 0;
if self.repeating() {
if self.mode == TimerMode::Repeating {
self.finished = false;
}
return self;
}

if !self.repeating() && self.finished() {
if self.mode != TimerMode::Repeating && self.finished() {
self.times_finished_this_tick = 0;
return self;
}
Expand All @@ -219,7 +219,7 @@ impl Timer {
self.finished = self.elapsed() >= self.duration();

if self.finished() {
if self.repeating() {
if self.mode == TimerMode::Repeating {
self.times_finished_this_tick =
(self.elapsed().as_nanos() / self.duration().as_nanos()) as u32;
// Duration does not have a modulo
Expand All @@ -243,7 +243,7 @@ impl Timer {
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, false);
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
/// timer.pause();
/// timer.tick(Duration::from_secs_f32(0.5));
/// assert_eq!(timer.elapsed_secs(), 0.0);
Expand All @@ -261,7 +261,7 @@ impl Timer {
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, false);
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
/// timer.pause();
/// timer.tick(Duration::from_secs_f32(0.5));
/// timer.unpause();
Expand All @@ -280,7 +280,7 @@ impl Timer {
/// # Examples
/// ```
/// # use bevy_time::*;
/// let mut timer = Timer::from_seconds(1.0, false);
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
/// assert!(!timer.paused());
/// timer.pause();
/// assert!(timer.paused());
Expand All @@ -300,7 +300,7 @@ impl Timer {
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, false);
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
/// timer.tick(Duration::from_secs_f32(1.5));
/// timer.reset();
/// assert!(!timer.finished());
Expand All @@ -319,7 +319,7 @@ impl Timer {
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(2.0, false);
/// let mut timer = Timer::from_seconds(2.0, TimerMode::Once);
/// timer.tick(Duration::from_secs_f32(0.5));
/// assert_eq!(timer.percent(), 0.25);
/// ```
Expand All @@ -334,7 +334,7 @@ impl Timer {
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(2.0, false);
/// let mut timer = Timer::from_seconds(2.0, TimerMode::Once);
/// timer.tick(Duration::from_secs_f32(0.5));
/// assert_eq!(timer.percent_left(), 0.75);
/// ```
Expand All @@ -350,7 +350,7 @@ impl Timer {
/// # use bevy_time::*;
/// use std::cmp::Ordering;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(2.0, false);
/// let mut timer = Timer::from_seconds(2.0, TimerMode::Once);
/// timer.tick(Duration::from_secs_f32(0.5));
/// let result = timer.remaining_secs().total_cmp(&1.5);
/// assert_eq!(Ordering::Equal, result);
Expand All @@ -366,7 +366,7 @@ impl Timer {
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(2.0, false);
/// let mut timer = Timer::from_seconds(2.0, TimerMode::Once);
/// timer.tick(Duration::from_secs_f32(0.5));
/// assert_eq!(timer.remaining(), Duration::from_secs_f32(1.5));
/// ```
Expand All @@ -385,7 +385,7 @@ impl Timer {
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, true);
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Repeating);
/// timer.tick(Duration::from_secs_f32(6.0));
/// assert_eq!(timer.times_finished_this_tick(), 6);
/// timer.tick(Duration::from_secs_f32(2.0));
Expand All @@ -399,22 +399,33 @@ impl Timer {
}
}

/// Specifies [`Timer`] behavior.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default, Reflect)]
#[reflect(Default)]
pub enum TimerMode {
/// Run once and stop.
#[default]
Once,
/// Reset when finished.
Repeating,
}

#[cfg(test)]
#[allow(clippy::float_cmp)]
mod tests {
use super::*;

#[test]
fn non_repeating_timer() {
let mut t = Timer::from_seconds(10.0, false);
let mut t = Timer::from_seconds(10.0, TimerMode::Once);
// Tick once, check all attributes
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!(!t.finished());
assert!(!t.just_finished());
assert_eq!(t.times_finished_this_tick(), 0);
assert!(!t.repeating());
assert_eq!(t.mode(), TimerMode::Once);
assert_eq!(t.percent(), 0.025);
assert_eq!(t.percent_left(), 0.975);
// Ticking while paused changes nothing
Expand All @@ -425,7 +436,7 @@ mod tests {
assert!(!t.finished());
assert!(!t.just_finished());
assert_eq!(t.times_finished_this_tick(), 0);
assert!(!t.repeating());
assert_eq!(t.mode(), TimerMode::Once);
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
Expand All @@ -449,15 +460,15 @@ mod tests {

#[test]
fn repeating_timer() {
let mut t = Timer::from_seconds(2.0, true);
let mut t = Timer::from_seconds(2.0, TimerMode::Repeating);
// Tick once, check all attributes
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!(!t.finished());
assert!(!t.just_finished());
assert_eq!(t.times_finished_this_tick(), 0);
assert!(t.repeating());
assert_eq!(t.mode(), TimerMode::Repeating);
assert_eq!(t.percent(), 0.375);
assert_eq!(t.percent_left(), 0.625);
// Tick past the end and make sure elapsed wraps
Expand All @@ -480,7 +491,7 @@ mod tests {

#[test]
fn times_finished_repeating() {
let mut t = Timer::from_seconds(1.0, true);
let mut t = Timer::from_seconds(1.0, TimerMode::Repeating);
assert_eq!(t.times_finished_this_tick(), 0);
t.tick(Duration::from_secs_f32(3.5));
assert_eq!(t.times_finished_this_tick(), 3);
Expand All @@ -493,7 +504,7 @@ mod tests {

#[test]
fn times_finished_this_tick() {
let mut t = Timer::from_seconds(1.0, false);
let mut t = Timer::from_seconds(1.0, TimerMode::Once);
assert_eq!(t.times_finished_this_tick(), 0);
t.tick(Duration::from_secs_f32(1.5));
assert_eq!(t.times_finished_this_tick(), 1);
Expand All @@ -503,7 +514,7 @@ mod tests {

#[test]
fn times_finished_this_tick_precise() {
let mut t = Timer::from_seconds(0.01, true);
let mut t = Timer::from_seconds(0.01, TimerMode::Repeating);
let duration = Duration::from_secs_f64(0.333);

// total duration: 0.333 => 33 times finished
Expand All @@ -522,7 +533,7 @@ mod tests {

#[test]
fn paused() {
let mut t = Timer::from_seconds(10.0, false);
let mut t = Timer::from_seconds(10.0, TimerMode::Once);

t.tick(Duration::from_secs_f32(10.0));
assert!(t.just_finished());
Expand All @@ -536,7 +547,7 @@ mod tests {

#[test]
fn paused_repeating() {
let mut t = Timer::from_seconds(10.0, true);
let mut t = Timer::from_seconds(10.0, TimerMode::Repeating);

t.tick(Duration::from_secs_f32(10.0));
assert!(t.just_finished());
Expand Down
2 changes: 1 addition & 1 deletion examples/2d/sprite_sheet.rs
Expand Up @@ -49,6 +49,6 @@ fn setup(
transform: Transform::from_scale(Vec3::splat(6.0)),
..default()
},
AnimationTimer(Timer::from_seconds(0.1, true)),
AnimationTimer(Timer::from_seconds(0.1, TimerMode::Repeating)),
));
}

0 comments on commit c242655

Please sign in to comment.