use crate::Stopwatch;
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::prelude::*;
use core::time::Duration;
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Default, Clone, PartialEq)
)]
pub struct Timer {
stopwatch: Stopwatch,
duration: Duration,
mode: TimerMode,
finished: bool,
times_finished_this_tick: u32,
}
impl Timer {
pub fn new(duration: Duration, mode: TimerMode) -> Self {
Self {
duration,
mode,
..Default::default()
}
}
pub fn from_seconds(duration: f32, mode: TimerMode) -> Self {
Self {
duration: Duration::from_secs_f32(duration),
mode,
..Default::default()
}
}
#[inline]
pub fn is_finished(&self) -> bool {
self.finished
}
#[inline]
pub fn just_finished(&self) -> bool {
self.times_finished_this_tick > 0
}
#[inline]
pub fn elapsed(&self) -> Duration {
self.stopwatch.elapsed()
}
#[inline]
pub fn elapsed_secs(&self) -> f32 {
self.stopwatch.elapsed_secs()
}
#[inline]
pub fn elapsed_secs_f64(&self) -> f64 {
self.stopwatch.elapsed_secs_f64()
}
#[inline]
pub fn set_elapsed(&mut self, time: Duration) {
self.stopwatch.set_elapsed(time);
}
#[inline]
pub fn duration(&self) -> Duration {
self.duration
}
#[inline]
pub fn set_duration(&mut self, duration: Duration) {
self.duration = duration;
}
#[inline]
pub fn finish(&mut self) {
let remaining = self.remaining();
self.tick(remaining);
}
#[inline]
pub fn almost_finish(&mut self) {
let remaining = self.remaining() - Duration::from_nanos(1);
self.tick(remaining);
}
#[inline]
pub fn mode(&self) -> TimerMode {
self.mode
}
#[doc(alias = "repeating")]
#[inline]
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.mode = mode;
}
pub fn tick(&mut self, delta: Duration) -> &Self {
if self.is_paused() {
self.times_finished_this_tick = 0;
if self.mode == TimerMode::Repeating {
self.finished = false;
}
return self;
}
if self.mode != TimerMode::Repeating && self.is_finished() {
self.times_finished_this_tick = 0;
return self;
}
self.stopwatch.tick(delta);
self.finished = self.elapsed() >= self.duration();
if self.is_finished() {
if self.mode == TimerMode::Repeating {
self.times_finished_this_tick = self
.elapsed()
.as_nanos()
.checked_div(self.duration().as_nanos())
.map_or(u32::MAX, |x| x as u32);
self.set_elapsed(
self.elapsed()
.as_nanos()
.checked_rem(self.duration().as_nanos())
.map_or(Duration::ZERO, |x| Duration::from_nanos(x as u64)),
);
} else {
self.times_finished_this_tick = 1;
self.set_elapsed(self.duration());
}
} else {
self.times_finished_this_tick = 0;
}
self
}
#[inline]
pub fn pause(&mut self) {
self.stopwatch.pause();
}
#[inline]
pub fn unpause(&mut self) {
self.stopwatch.unpause();
}
#[inline]
pub fn is_paused(&self) -> bool {
self.stopwatch.is_paused()
}
pub fn reset(&mut self) {
self.stopwatch.reset();
self.finished = false;
self.times_finished_this_tick = 0;
}
#[inline]
pub fn fraction(&self) -> f32 {
if self.duration == Duration::ZERO {
1.0
} else {
self.elapsed().as_secs_f32() / self.duration().as_secs_f32()
}
}
#[inline]
pub fn fraction_remaining(&self) -> f32 {
1.0 - self.fraction()
}
#[inline]
pub fn remaining_secs(&self) -> f32 {
self.remaining().as_secs_f32()
}
#[inline]
pub fn remaining(&self) -> Duration {
self.duration() - self.elapsed()
}
#[inline]
pub fn times_finished_this_tick(&self) -> u32 {
self.times_finished_this_tick
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default)]
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Default, Clone, PartialEq, Hash)
)]
pub enum TimerMode {
#[default]
Once,
Repeating,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn non_repeating_timer() {
let mut t = Timer::from_seconds(10.0, TimerMode::Once);
t.tick(Duration::from_secs_f32(0.25));
assert_eq!(t.elapsed_secs(), 0.25);
assert_eq!(t.elapsed_secs_f64(), 0.25);
assert_eq!(t.duration(), Duration::from_secs_f32(10.0));
assert!(!t.is_finished());
assert!(!t.just_finished());
assert_eq!(t.times_finished_this_tick(), 0);
assert_eq!(t.mode(), TimerMode::Once);
assert_eq!(t.fraction(), 0.025);
assert_eq!(t.fraction_remaining(), 0.975);
t.pause();
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!(!t.is_finished());
assert!(!t.just_finished());
assert_eq!(t.times_finished_this_tick(), 0);
assert_eq!(t.mode(), TimerMode::Once);
assert_eq!(t.fraction(), 0.025);
assert_eq!(t.fraction_remaining(), 0.975);
t.unpause();
t.tick(Duration::from_secs_f32(500.0));
assert_eq!(t.elapsed_secs(), 10.0);
assert_eq!(t.elapsed_secs_f64(), 10.0);
assert!(t.is_finished());
assert!(t.just_finished());
assert_eq!(t.times_finished_this_tick(), 1);
assert_eq!(t.fraction(), 1.0);
assert_eq!(t.fraction_remaining(), 0.0);
t.tick(Duration::from_secs_f32(1.0));
assert_eq!(t.elapsed_secs(), 10.0);
assert_eq!(t.elapsed_secs_f64(), 10.0);
assert!(t.is_finished());
assert!(!t.just_finished());
assert_eq!(t.times_finished_this_tick(), 0);
assert_eq!(t.fraction(), 1.0);
assert_eq!(t.fraction_remaining(), 0.0);
}
#[test]
fn repeating_timer() {
let mut t = Timer::from_seconds(2.0, TimerMode::Repeating);
t.tick(Duration::from_secs_f32(0.75));
assert_eq!(t.elapsed_secs(), 0.75);
assert_eq!(t.elapsed_secs_f64(), 0.75);
assert_eq!(t.duration(), Duration::from_secs_f32(2.0));
assert!(!t.is_finished());
assert!(!t.just_finished());
assert_eq!(t.times_finished_this_tick(), 0);
assert_eq!(t.mode(), TimerMode::Repeating);
assert_eq!(t.fraction(), 0.375);
assert_eq!(t.fraction_remaining(), 0.625);
t.tick(Duration::from_secs_f32(1.5));
assert_eq!(t.elapsed_secs(), 0.25);
assert_eq!(t.elapsed_secs_f64(), 0.25);
assert!(t.is_finished());
assert!(t.just_finished());
assert_eq!(t.times_finished_this_tick(), 1);
assert_eq!(t.fraction(), 0.125);
assert_eq!(t.fraction_remaining(), 0.875);
t.tick(Duration::from_secs_f32(1.0));
assert_eq!(t.elapsed_secs(), 1.25);
assert_eq!(t.elapsed_secs_f64(), 1.25);
assert!(!t.is_finished());
assert!(!t.just_finished());
assert_eq!(t.times_finished_this_tick(), 0);
assert_eq!(t.fraction(), 0.625);
assert_eq!(t.fraction_remaining(), 0.375);
}
#[test]
fn times_finished_repeating() {
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);
assert_eq!(t.elapsed_secs(), 0.5);
assert_eq!(t.elapsed_secs_f64(), 0.5);
assert!(t.is_finished());
assert!(t.just_finished());
t.tick(Duration::from_secs_f32(0.2));
assert_eq!(t.times_finished_this_tick(), 0);
}
#[test]
fn times_finished_this_tick() {
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);
t.tick(Duration::from_secs_f32(0.5));
assert_eq!(t.times_finished_this_tick(), 0);
}
#[test]
fn times_finished_this_tick_repeating_zero_duration() {
let mut t = Timer::from_seconds(0.0, TimerMode::Repeating);
assert_eq!(t.times_finished_this_tick(), 0);
assert_eq!(t.elapsed(), Duration::ZERO);
assert_eq!(t.fraction(), 1.0);
t.tick(Duration::from_secs(1));
assert_eq!(t.times_finished_this_tick(), u32::MAX);
assert_eq!(t.elapsed(), Duration::ZERO);
assert_eq!(t.fraction(), 1.0);
t.tick(Duration::from_secs(2));
assert_eq!(t.times_finished_this_tick(), u32::MAX);
assert_eq!(t.elapsed(), Duration::ZERO);
assert_eq!(t.fraction(), 1.0);
t.reset();
assert_eq!(t.times_finished_this_tick(), 0);
assert_eq!(t.elapsed(), Duration::ZERO);
assert_eq!(t.fraction(), 1.0);
}
#[test]
fn times_finished_this_tick_precise() {
let mut t = Timer::from_seconds(0.01, TimerMode::Repeating);
let duration = Duration::from_secs_f64(0.333);
t.tick(duration);
assert_eq!(t.times_finished_this_tick(), 33);
t.tick(duration);
assert_eq!(t.times_finished_this_tick(), 33);
t.tick(duration);
assert_eq!(t.times_finished_this_tick(), 33);
t.tick(duration);
assert_eq!(t.times_finished_this_tick(), 34);
}
#[test]
fn almost_finished_repeating() {
let mut t = Timer::from_seconds(10.0, TimerMode::Repeating);
let duration = Duration::from_nanos(1);
t.almost_finish();
assert!(!t.is_finished());
assert_eq!(t.times_finished_this_tick(), 0);
assert_eq!(t.remaining(), Duration::from_nanos(1));
t.tick(duration);
assert!(t.is_finished());
assert_eq!(t.times_finished_this_tick(), 1);
}
#[test]
fn paused() {
let mut t = Timer::from_seconds(10.0, TimerMode::Once);
t.tick(Duration::from_secs_f32(10.0));
assert!(t.just_finished());
assert!(t.is_finished());
t.pause();
t.tick(Duration::from_secs_f32(5.0));
assert!(!t.just_finished());
assert!(t.is_finished());
}
#[test]
fn paused_repeating() {
let mut t = Timer::from_seconds(10.0, TimerMode::Repeating);
t.tick(Duration::from_secs_f32(10.0));
assert!(t.just_finished());
assert!(t.is_finished());
t.pause();
t.tick(Duration::from_secs_f32(5.0));
assert!(!t.just_finished());
assert!(!t.is_finished());
}
}