Timer Polishing (#931)

* Pause stops ticks. Consistent getter method names. Update tests.

* Add timing example

* Format with the nightly formatter

Co-authored-by: Amber Kowalski <amberkowalski03@gmail.com>
This commit is contained in:
Nathan Stocks 2020-11-27 12:39:33 -07:00 committed by GitHub
parent f69cc6f94c
commit 12f29bd38c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 126 additions and 33 deletions

View file

@ -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

View file

@ -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"

View file

@ -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
}
@ -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
}
@ -87,7 +87,7 @@ impl Timer {
}
#[inline]
pub fn is_repeating(&self) -> bool {
pub fn repeating(&self) -> bool {
self.repeating
}
@ -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
@ -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);
}
@ -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);
}

View file

@ -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 {
@ -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 {

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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(),
});

73
examples/ecs/timers.rs Normal file
View file

@ -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")
}
}
}

View file

@ -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) {