Skip to content

Commit

Permalink
Timer Polishing (#931)
Browse files Browse the repository at this point in the history
* Pause stops ticks. Consistent getter method names. Update tests.

* Add timing example

* Format with the nightly formatter

Co-authored-by: Amber Kowalski <[email protected]>
  • Loading branch information
CleanCut and ambeeeeee authored Nov 27, 2020
1 parent f69cc6f commit 12f29bd
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 33 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ current changes on git with [previous release tags][git_tag_comparison].
### Added

### Changed

- [Breaking changes to timer API][914]
- [Removed timer auto-ticking system][931]
### Fixed


Expand Down
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@ path = "examples/ecs/startup_system.rs"
name = "system_chaining"
path = "examples/ecs/system_chaining.rs"

[[example]]
name = "timers"
path = "examples/ecs/timers.rs"

[[example]]
name = "ecs_guide"
path = "examples/ecs/ecs_guide.rs"
Expand Down
67 changes: 41 additions & 26 deletions crates/bevy_core/src/time/timer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ impl Timer {
}

#[inline]
pub fn resume(&mut self) {
pub fn unpause(&mut self) {
self.paused = false
}

#[inline]
pub fn is_paused(&self) -> bool {
pub fn paused(&self) -> bool {
self.paused
}

Expand Down Expand Up @@ -76,7 +76,7 @@ impl Timer {
/// 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, and can still be reset at any given point.
#[inline]
pub fn is_finished(&self) -> bool {
pub fn finished(&self) -> bool {
self.finished
}

Expand All @@ -87,7 +87,7 @@ impl Timer {
}

#[inline]
pub fn is_repeating(&self) -> bool {
pub fn repeating(&self) -> bool {
self.repeating
}

Expand All @@ -98,11 +98,15 @@ impl Timer {

/// Advances the timer by `delta` seconds.
pub fn tick(&mut self, delta: f32) -> &Self {
if self.paused {
return self;
}
let prev_finished = self.finished;
self.elapsed += delta;

self.finished = self.elapsed >= self.duration;
self.just_finished = !prev_finished && self.finished;

if self.finished {
if self.repeating {
// Repeating timers wrap around
Expand Down Expand Up @@ -142,25 +146,36 @@ mod tests {
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);
assert_eq!(t.finished, false);
assert_eq!(t.just_finished, false);
assert_eq!(t.repeating, false);
assert_eq!(t.elapsed(), 0.25);
assert_eq!(t.duration(), 10.0);
assert_eq!(t.finished(), false);
assert_eq!(t.just_finished(), false);
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);
assert_eq!(t.finished(), false);
assert_eq!(t.just_finished(), false);
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);
assert_eq!(t.finished, true);
assert_eq!(t.just_finished, true);
assert_eq!(t.elapsed(), 10.0);
assert_eq!(t.finished(), true);
assert_eq!(t.just_finished(), true);
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);
assert_eq!(t.finished, true);
assert_eq!(t.just_finished, false);
assert_eq!(t.elapsed(), 10.0);
assert_eq!(t.finished(), true);
assert_eq!(t.just_finished(), false);
assert_eq!(t.percent(), 1.0);
assert_eq!(t.percent_left(), 0.0);
}
Expand All @@ -170,25 +185,25 @@ mod tests {
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);
assert_eq!(t.finished, false);
assert_eq!(t.just_finished, false);
assert_eq!(t.repeating, true);
assert_eq!(t.elapsed(), 0.75);
assert_eq!(t.duration(), 2.0);
assert_eq!(t.finished(), false);
assert_eq!(t.just_finished(), false);
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(1.5);
assert_eq!(t.elapsed, 0.25);
assert_eq!(t.finished, true);
assert_eq!(t.just_finished, true);
assert_eq!(t.elapsed(), 0.25);
assert_eq!(t.finished(), true);
assert_eq!(t.just_finished(), true);
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);
assert_eq!(t.finished, false);
assert_eq!(t.just_finished, false);
assert_eq!(t.elapsed(), 1.25);
assert_eq!(t.finished(), false);
assert_eq!(t.just_finished(), false);
assert_eq!(t.percent(), 0.625);
assert_eq!(t.percent_left(), 0.375);
}
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_diagnostic/src/print_diagnostics_plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl PrintDiagnosticsPlugin {
time: Res<Time>,
diagnostics: Res<Diagnostics>,
) {
if state.timer.tick(time.delta_seconds).is_finished() {
if state.timer.tick(time.delta_seconds).finished() {
println!("Diagnostics:");
println!("{}", "-".repeat(93));
if let Some(ref filter) = state.filter {
Expand All @@ -86,7 +86,7 @@ impl PrintDiagnosticsPlugin {
time: Res<Time>,
diagnostics: Res<Diagnostics>,
) {
if state.timer.tick(time.delta_seconds).is_finished() {
if state.timer.tick(time.delta_seconds).finished() {
println!("Diagnostics (Debug):");
println!("{}", "-".repeat(93));
if let Some(ref filter) = state.filter {
Expand Down
2 changes: 1 addition & 1 deletion examples/2d/sprite_sheet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ fn animate_sprite_system(
mut query: Query<(&mut Timer, &mut TextureAtlasSprite, &Handle<TextureAtlas>)>,
) {
for (timer, mut sprite, texture_atlas_handle) in query.iter_mut() {
if timer.is_finished() {
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;
}
Expand Down
2 changes: 1 addition & 1 deletion examples/app/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ struct PrintMessageState {
}

fn print_message_system(mut state: ResMut<PrintMessageState>, time: Res<Time>) {
if state.timer.tick(time.delta_seconds).is_finished() {
if state.timer.tick(time.delta_seconds).finished() {
println!("{}", state.message);
}
}
2 changes: 1 addition & 1 deletion examples/ecs/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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).is_finished() {
if state.event_timer.tick(time.delta_seconds).finished() {
my_events.send(MyEvent {
message: "MyEvent just happened!".to_string(),
});
Expand Down
73 changes: 73 additions & 0 deletions examples/ecs/timers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use bevy::{log::info, prelude::*};

fn main() {
App::build()
.add_plugins(DefaultPlugins)
.add_resource(Countdown::default())
.add_startup_system(setup_system)
.add_system(countdown_system)
.add_system(timer_system)
.run();
}

pub struct Countdown {
pub percent_trigger: Timer,
pub main_timer: Timer,
}

impl Countdown {
pub fn new() -> Self {
Self {
percent_trigger: Timer::from_seconds(4.0, true),
main_timer: Timer::from_seconds(20.0, false),
}
}
}

impl Default for Countdown {
fn default() -> Self {
Self::new()
}
}

fn setup_system(commands: &mut Commands) {
// Add an entity to the world with a timer
commands.spawn((Timer::from_seconds(5.0, false),));
}

/// This system ticks all the `Timer` components on entities within the scene
/// 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() {
info!("Entity timer just finished")
}
}
}

/// 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);

// 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.main_timer.finished() {
// Print the percent complete the main timer is.
info!(
"Timer is {:0.0}% complete!",
countdown.main_timer.percent() * 100.0
);
} else {
// The timer has finished so we pause the percent output timer
countdown.percent_trigger.pause();
info!("Paused percent trigger timer")
}
}
}
2 changes: 1 addition & 1 deletion examples/ui/font_atlas_debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,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).is_finished() {
if state.timer.tick(time.delta_seconds).finished() {
for mut text in query.iter_mut() {
let c = rand::random::<u8>() as char;
if !text.value.contains(c) {
Expand Down

0 comments on commit 12f29bd

Please sign in to comment.