Skip to content

Commit

Permalink
Refactor Time API and internals (#934)
Browse files Browse the repository at this point in the history
Refactor Time API and internals
  • Loading branch information
ambeeeeee authored Nov 28, 2020
1 parent f3b49e4 commit 097a559
Show file tree
Hide file tree
Showing 18 changed files with 146 additions and 40 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,18 @@ current changes on git with [previous release tags][git_tag_comparison].

### Changed
- [Breaking changes to timer API][914]
- Created getters and setters rather than exposing struct members.
- [Removed timer auto-ticking system][931]
- Added an example of how to tick timers manually.
- [Breaking changes to Time API][934]
- Created getters to get `Time` state and made members private.
- Modifying `Time`'s values directly is no longer possible outside of bevy.
### Fixed

[914]: https://github.com/bevyengine/bevy/pull/914
[931]: https://github.com/bevyengine/bevy/pull/931
[934]: https://github.com/bevyengine/bevy/pull/934


## Version 0.3.0 (2020-11-03)

Expand Down
119 changes: 108 additions & 11 deletions crates/bevy_core/src/time/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ use bevy_utils::{Duration, Instant};
/// Tracks elapsed time since the last update and since the App has started
#[derive(Debug)]
pub struct Time {
pub delta: Duration,
pub instant: Option<Instant>,
pub delta_seconds_f64: f64,
pub delta_seconds: f32,
pub seconds_since_startup: f64,
pub startup: Instant,
delta: Duration,
last_update: Option<Instant>,
delta_seconds_f64: f64,
delta_seconds: f32,
seconds_since_startup: f64,
startup: Instant,
}

impl Default for Time {
fn default() -> Time {
Time {
delta: Duration::from_secs(0),
instant: None,
last_update: None,
startup: Instant::now(),
delta_seconds_f64: 0.0,
seconds_since_startup: 0.0,
Expand All @@ -28,15 +28,55 @@ impl Default for Time {
impl Time {
pub fn update(&mut self) {
let now = Instant::now();
if let Some(instant) = self.instant {
self.delta = now - instant;
self.update_with_instant(now);
}

pub(crate) fn update_with_instant(&mut self, instant: Instant) {
if let Some(last_update) = self.last_update {
self.delta = instant - last_update;
self.delta_seconds_f64 = self.delta.as_secs_f64();
self.delta_seconds = self.delta.as_secs_f32();
}

let duration_since_startup = now - self.startup;
let duration_since_startup = instant - self.startup;
self.seconds_since_startup = duration_since_startup.as_secs_f64();
self.instant = Some(now);
self.last_update = Some(instant);
}

/// The delta between the current tick and last tick as a [`Duration`]
#[inline]
pub fn delta(&self) -> Duration {
self.delta
}

/// The delta between the current and last tick as [`f32`] seconds
#[inline]
pub fn delta_seconds(&self) -> f32 {
self.delta_seconds
}

/// The delta between the current and last tick as [`f64`] seconds
#[inline]
pub fn delta_seconds_f64(&self) -> f64 {
self.delta_seconds_f64
}

/// The time since startup in seconds
#[inline]
pub fn seconds_since_startup(&self) -> f64 {
self.seconds_since_startup
}

/// The [`Instant`] the app was started
#[inline]
pub fn startup(&self) -> Instant {
self.startup
}

/// The ['Instant'] when [`Time::update`] was last called, if it exists
#[inline]
pub fn last_update(&self) -> Option<Instant> {
self.last_update
}

pub fn time_since_startup(&self) -> Duration {
Expand All @@ -47,3 +87,60 @@ impl Time {
pub(crate) fn time_system(mut time: ResMut<Time>) {
time.update();
}

#[cfg(test)]
mod tests {
use super::Time;
use bevy_utils::{Duration, Instant};

#[test]
fn update_test() {
let start_instant = Instant::now();

// Create a `Time` for testing
let mut time = Time {
startup: start_instant,
..Default::default()
};

// Ensure `time` was constructed correctly
assert_eq!(time.delta(), Duration::from_secs(0));
assert_eq!(time.last_update(), None);
assert_eq!(time.startup(), start_instant);
assert_eq!(time.delta_seconds_f64(), 0.0);
assert_eq!(time.seconds_since_startup(), 0.0);
assert_eq!(time.delta_seconds(), 0.0);

// Update `time` and check results
let first_update_instant = Instant::now();

time.update_with_instant(first_update_instant);

assert_eq!(time.delta(), Duration::from_secs(0));
assert_eq!(time.last_update(), Some(first_update_instant));
assert_eq!(time.startup(), start_instant);
assert_eq!(time.delta_seconds_f64(), 0.0);
assert_eq!(
time.seconds_since_startup(),
(first_update_instant - start_instant).as_secs_f64()
);
assert_eq!(time.delta_seconds, 0.0);

// Update `time` again and check results
let second_update_instant = Instant::now();

time.update_with_instant(second_update_instant);

assert_eq!(time.delta(), second_update_instant - first_update_instant);
assert_eq!(time.last_update(), Some(second_update_instant));
assert_eq!(time.startup(), start_instant);
// At this point its safe to use time.delta as a valid value
// because it's been previously verified to be correct
assert_eq!(time.delta_seconds_f64(), time.delta().as_secs_f64());
assert_eq!(
time.seconds_since_startup(),
(second_update_instant - start_instant).as_secs_f64()
);
assert_eq!(time.delta_seconds(), time.delta().as_secs_f32());
}
}
4 changes: 2 additions & 2 deletions crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ impl FrameTimeDiagnosticsPlugin {
state.frame_count += 1.0;
diagnostics.add_measurement(Self::FRAME_COUNT, state.frame_count);

if time.delta_seconds_f64 == 0.0 {
if time.delta_seconds_f64() == 0.0 {
return;
}

diagnostics.add_measurement(Self::FRAME_TIME, time.delta_seconds_f64);
diagnostics.add_measurement(Self::FRAME_TIME, time.delta_seconds_f64());
if let Some(fps) = diagnostics
.get(Self::FRAME_TIME)
.and_then(|frame_time_diagnostic| {
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).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).finished() {
if state.timer.tick(time.delta_seconds()).finished() {
println!("Diagnostics (Debug):");
println!("{}", "-".repeat(93));
if let Some(ref filter) = state.filter {
Expand Down
4 changes: 2 additions & 2 deletions examples/2d/contributors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ fn deselect(

/// Applies gravity to all entities with velocity
fn velocity_system(time: Res<Time>, mut q: Query<Mut<Velocity>>) {
let delta = time.delta_seconds;
let delta = time.delta_seconds();

for mut v in q.iter_mut() {
v.translation += Vec3::new(0.0, GRAVITY * delta, 0.0);
Expand Down Expand Up @@ -274,7 +274,7 @@ fn collision_system(

/// Apply velocity to positions and rotations.
fn move_system(time: Res<Time>, mut q: Query<(&Velocity, Mut<Transform>)>) {
let delta = time.delta_seconds;
let delta = time.delta_seconds();

for (v, mut t) in q.iter_mut() {
t.translation += delta * v.translation;
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 @@ -14,7 +14,7 @@ fn animate_sprite_system(
mut query: Query<(&mut Timer, &mut TextureAtlasSprite, &Handle<TextureAtlas>)>,
) {
for (mut timer, mut sprite, texture_atlas_handle) in query.iter_mut() {
timer.tick(time.delta_seconds);
timer.tick(time.delta_seconds());
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/3d/parenting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ struct Rotator;
/// rotates the parent, which will result in the child also rotating
fn rotator_system(time: Res<Time>, mut query: Query<&mut Transform, With<Rotator>>) {
for mut transform in query.iter_mut() {
transform.rotation *= Quat::from_rotation_x(3.0 * time.delta_seconds);
transform.rotation *= Quat::from_rotation_x(3.0 * time.delta_seconds());
}
}

Expand Down
4 changes: 2 additions & 2 deletions examples/3d/spawner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ fn move_cubes(
) {
for (mut transform, material_handle) in query.iter_mut() {
let material = materials.get_mut(material_handle).unwrap();
transform.translation += Vec3::new(1.0, 0.0, 0.0) * time.delta_seconds;
transform.translation += Vec3::new(1.0, 0.0, 0.0) * time.delta_seconds();
material.albedo =
Color::BLUE * Vec3::splat((3.0 * time.seconds_since_startup as f32).sin());
Color::BLUE * Vec3::splat((3.0 * time.seconds_since_startup() as f32).sin());
}
}

Expand Down
2 changes: 1 addition & 1 deletion examples/3d/z_sort_debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ struct Rotator;
/// rotates the parent, which will result in the child also rotating
fn rotator_system(time: Res<Time>, mut query: Query<&mut Transform, With<Rotator>>) {
for mut transform in query.iter_mut() {
transform.rotation *= Quat::from_rotation_x(3.0 * time.delta_seconds);
transform.rotation *= Quat::from_rotation_x(3.0 * time.delta_seconds());
}
}

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).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).finished() {
if state.event_timer.tick(time.delta_seconds()).finished() {
my_events.send(MyEvent {
message: "MyEvent just happened!".to_string(),
});
Expand Down
8 changes: 4 additions & 4 deletions examples/ecs/hierarchy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,24 +97,24 @@ fn rotate(
let angle = std::f32::consts::PI / 2.0;
for (parent, children) in parents_query.iter_mut() {
if let Ok(mut transform) = transform_query.get_mut(parent) {
transform.rotate(Quat::from_rotation_z(-angle * time.delta_seconds));
transform.rotate(Quat::from_rotation_z(-angle * time.delta_seconds()));
}

// To iterate through the entities children, just treat the Children component as a Vec
// Alternatively, you could query entities that have a Parent component
for child in children.iter() {
if let Ok(mut transform) = transform_query.get_mut(*child) {
transform.rotate(Quat::from_rotation_z(angle * 2.0 * time.delta_seconds));
transform.rotate(Quat::from_rotation_z(angle * 2.0 * time.delta_seconds()));
}
}

// To demonstrate removing children, we'll start to remove the children after a couple of seconds
if time.seconds_since_startup >= 2.0 && children.len() == 3 {
if time.seconds_since_startup() >= 2.0 && children.len() == 3 {
let child = children.last().copied().unwrap();
commands.despawn(child);
}

if time.seconds_since_startup >= 4.0 {
if time.seconds_since_startup() >= 4.0 {
// This will remove the entity from its parent's list of children, as well as despawn
// any children the entity has.
commands.despawn_recursive(parent);
Expand Down
6 changes: 3 additions & 3 deletions examples/ecs/timers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ fn setup_system(commands: &mut Commands) {
/// 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() {
if timer.tick(time.delta_seconds()).just_finished() {
info!("Entity timer just finished")
}
}
Expand All @@ -48,14 +48,14 @@ fn timer_system(time: Res<Time>, mut query: Query<&mut Timer>) {
/// 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);
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)
.tick(time.delta_seconds())
.just_finished()
{
if !countdown.main_timer.finished() {
Expand Down
4 changes: 2 additions & 2 deletions examples/game/breakout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,15 +174,15 @@ fn paddle_movement_system(

let translation = &mut transform.translation;
// move the paddle horizontally
translation.x += time.delta_seconds * direction * paddle.speed;
translation.x += time.delta_seconds() * direction * paddle.speed;
// bound the paddle within the walls
translation.x = translation.x.min(380.0).max(-380.0);
}
}

fn ball_movement_system(time: Res<Time>, mut ball_query: Query<(&Ball, &mut Transform)>) {
// clamp the timestep to stop the ball from escaping when the game starts
let delta_seconds = f32::min(0.2, time.delta_seconds);
let delta_seconds = f32::min(0.2, time.delta_seconds());

for (ball, mut transform) in ball_query.iter_mut() {
transform.translation += ball.velocity * delta_seconds;
Expand Down
8 changes: 4 additions & 4 deletions examples/tools/bevymark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ fn mouse_handler(
mut counter: ResMut<BevyCounter>,
) {
if mouse_button_input.pressed(MouseButton::Left) {
let spawn_count = (BIRDS_PER_SECOND as f32 * time.delta_seconds) as u128;
let spawn_count = (BIRDS_PER_SECOND as f32 * time.delta_seconds()) as u128;
let bird_x = (window.width as i32 / -2) as f32 + HALF_BIRD_SIZE;
let bird_y = (window.height / 2) as f32 - HALF_BIRD_SIZE;

Expand Down Expand Up @@ -118,9 +118,9 @@ fn mouse_handler(

fn movement_system(time: Res<Time>, mut bird_query: Query<(&mut Bird, &mut Transform)>) {
for (mut bird, mut transform) in bird_query.iter_mut() {
transform.translation.x += bird.velocity.x * time.delta_seconds;
transform.translation.y += bird.velocity.y * time.delta_seconds;
bird.velocity.y += GRAVITY * time.delta_seconds;
transform.translation.x += bird.velocity.x * time.delta_seconds();
transform.translation.y += bird.velocity.y * time.delta_seconds();
bird.velocity.y += GRAVITY * time.delta_seconds();
}
}

Expand Down
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).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
2 changes: 1 addition & 1 deletion examples/wasm/winit_wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ fn counter(mut state: Local<CounterState>, time: Res<Time>) {
"tick {} @ {:?} [Δ{}]",
state.count,
time.time_since_startup(),
time.delta_seconds
time.delta_seconds()
);
}
state.count += 1;
Expand Down
2 changes: 1 addition & 1 deletion examples/window/window_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ fn change_title(time: Res<Time>, mut windows: ResMut<Windows>) {
let window = windows.get_primary_mut().unwrap();
window.set_title(format!(
"Seconds since startup: {}",
time.seconds_since_startup.round()
time.seconds_since_startup().round()
));
}

Expand Down

0 comments on commit 097a559

Please sign in to comment.