Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Time API and internals #934

Merged
merged 8 commits into from
Nov 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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]
ambeeeeee marked this conversation as resolved.
Show resolved Hide resolved
- 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