Fix timer with zero duration (#8467)
# Objective Timer with zero `Duration` panics at `tick()` because of division by zero. This PR Fixes #8463 . ## Solution - Handle division by zero separately with `checked_div` and `checked_rem`. --- ## Changelog - Replace division with `checked_div`. Set `times_finished_this_tick` to u32::MAX when duration is zero. - Set `elapsed` to `Duration::ZERO` when timer duration is zero. - Set `percent` to `1.0` when duration is zero. - `times_finished_this_tick` is [not used anywhere](https://github.com/bevyengine/bevy/search?q=times_finished_this_tick), that's why this change will not affect other parts of the project. - `times_finished_this_tick` is set to `0` after `reset()` and before first `tick()` call.
This commit is contained in:
parent
f3360938eb
commit
e2531b2273
@ -224,10 +224,17 @@ impl Timer {
|
||||
|
||||
if self.finished() {
|
||||
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
|
||||
self.set_elapsed(self.elapsed() - self.duration() * self.times_finished_this_tick);
|
||||
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());
|
||||
@ -329,8 +336,12 @@ impl Timer {
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn percent(&self) -> f32 {
|
||||
if self.duration == Duration::ZERO {
|
||||
1.0
|
||||
} else {
|
||||
self.elapsed().as_secs_f32() / self.duration().as_secs_f32()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the percentage of the timer remaining time (goes from 1.0 to 0.0).
|
||||
///
|
||||
@ -517,6 +528,26 @@ mod tests {
|
||||
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.percent(), 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.percent(), 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.percent(), 1.0);
|
||||
t.reset();
|
||||
assert_eq!(t.times_finished_this_tick(), 0);
|
||||
assert_eq!(t.elapsed(), Duration::ZERO);
|
||||
assert_eq!(t.percent(), 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn times_finished_this_tick_precise() {
|
||||
let mut t = Timer::from_seconds(0.01, TimerMode::Repeating);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user