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

Make Time update_with_instant pub #2549

Closed
Closed
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
3 changes: 2 additions & 1 deletion crates/bevy_core/src/time/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ impl Time {
self.update_with_instant(now);
}

pub(crate) fn update_with_instant(&mut self, instant: Instant) {
/// Update time with a specified [`Instant`], for use in tests. Should never be called on the default Time resource.
Copy link
Member

@alice-i-cecile alice-i-cecile Apr 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Update time with a specified [`Instant`], for use in tests. Should never be called on the default Time resource.
/// Update time with a specified [`Instant`]
///
/// This method is for use in tests, and should generally not be called on the [`Time`] resource as part of your app.
/// Doing so will conflict with the `time_system` called by [`CorePlugin`], and result in inaccurate performance measurements.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just inaccurate timekeeping in general. I would keep the recommendation to only use this to write tests.

pub 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();
Expand Down
75 changes: 74 additions & 1 deletion tests/how_to_test_systems.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
use bevy::prelude::*;
use bevy::{
math::vec2,
prelude::*,
utils::{Duration, Instant},
};

#[derive(Default)]
struct Enemy {
hit_points: u32,
}

#[derive(Default)]
struct Velocity(Vec2);

#[derive(Default, Clone, Copy)]
struct Position(Vec2);

fn despawn_dead_enemies(mut commands: Commands, enemies: Query<(Entity, &Enemy)>) {
for (entity, enemy) in enemies.iter() {
if enemy.hit_points == 0 {
Expand All @@ -25,6 +35,12 @@ fn spawn_enemy(mut commands: Commands, keyboard_input: Res<Input<KeyCode>>) {
}
}

fn update_position(time: Res<Time>, mut units: Query<(&Velocity, &mut Position)>) {
for (velocity, mut position) in units.iter_mut() {
position.0 += velocity.0 * time.delta_seconds();
}
}

#[test]
fn did_hurt_enemy() {
// Setup world
Expand Down Expand Up @@ -95,3 +111,60 @@ fn spawn_enemy_using_input_resource() {
// Check resulting changes, no new entity has been spawned
assert_eq!(world.query::<&Enemy>().iter(&world).len(), 1);
}

#[test]
fn confirm_system_is_framerate_independent() {
// Setup world
let mut world = World::default();

// Setup stage with a system
let mut update_stage = SystemStage::parallel();
update_stage.add_system(update_position.system());

// Closure that gets the resulting position for a certain fps
let mut test_fps = |fps: u32| -> Position {
// The frame time delta we want to simulate
let delta = Duration::from_secs_f32(1.0 / fps as f32);

// Setup test entities
let entity = world
.spawn()
.insert_bundle((Velocity(vec2(1.0, 0.0)), Position(vec2(0.0, 0.0))))
.id();

// Setup test resource
let time = Time::default();
world.insert_resource(time);

// Set initial time
let mut time = world.get_resource_mut::<Time>().unwrap();
let initial_instant = Instant::now();
time.update_with_instant(initial_instant);

// Simulate one second
for i in 0..fps + 1 {
// Update time
let mut time = world.get_resource_mut::<Time>().unwrap();
time.update_with_instant(initial_instant + delta * i);

// Run systems
update_stage.run(&mut world);
}

// Remove mocked Time
world.remove_resource::<Time>();

// Return resulting position
*world.get_entity(entity).unwrap().get::<Position>().unwrap()
};

// Test at 30 and 60 fps
let result_30fps = test_fps(30);
let result_60fps = test_fps(60);
Comment on lines +162 to +163

This comment was marked as outdated.


// Calculate the difference
let difference = (result_30fps.0 - result_60fps.0).length();

// A tiny difference is expected due to f32 precision
assert!(difference < f32::EPSILON * 10.0);
}