Skip to content

Commit 6f74262

Browse files
committed
Split Time into three separate clocks
1 parent 910f984 commit 6f74262

File tree

7 files changed

+290
-846
lines changed

7 files changed

+290
-846
lines changed

crates/bevy_time/src/common_conditions.rs

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{fixed_timestep::FixedTime, Time, Timer, TimerMode};
1+
use crate::{Time, Timer, TimerMode};
22
use bevy_ecs::system::Res;
33
use bevy_utils::Duration;
44

@@ -40,40 +40,6 @@ pub fn on_timer(duration: Duration) -> impl FnMut(Res<Time>) -> bool + Clone {
4040
}
4141
}
4242

43-
/// Run condition that is active on a regular time interval, using [`FixedTime`] to
44-
/// advance the timer.
45-
///
46-
/// If used for a non-fixed timestep system, use [`on_timer`] instead.
47-
///
48-
/// ```rust,no_run
49-
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, FixedUpdate};
50-
/// # use bevy_ecs::schedule::IntoSystemConfigs;
51-
/// # use bevy_utils::Duration;
52-
/// # use bevy_time::common_conditions::on_fixed_timer;
53-
/// fn main() {
54-
/// App::new()
55-
/// .add_plugins(DefaultPlugins)
56-
/// .add_systems(FixedUpdate,
57-
/// tick.run_if(on_fixed_timer(Duration::from_secs(1))),
58-
/// )
59-
/// .run();
60-
/// }
61-
/// fn tick() {
62-
/// // ran once a second
63-
/// }
64-
/// ```
65-
///
66-
/// Note that this run condition may not behave as expected if `duration` is smaller
67-
/// than the fixed timestep period, since the timer may complete multiple times in
68-
/// one fixed update.
69-
pub fn on_fixed_timer(duration: Duration) -> impl FnMut(Res<FixedTime>) -> bool + Clone {
70-
let mut timer = Timer::new(duration, TimerMode::Repeating);
71-
move |time: Res<FixedTime>| {
72-
timer.tick(time.period);
73-
timer.just_finished()
74-
}
75-
}
76-
7743
#[cfg(test)]
7844
mod tests {
7945
use super::*;

crates/bevy_time/src/fixed.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use bevy_ecs::world::World;
2+
use bevy_ecs::{reflect::ReflectResource, system::Resource};
3+
use bevy_reflect::{FromReflect, Reflect};
4+
use bevy_utils::Duration;
5+
6+
use crate::time::Time;
7+
use crate::virt::Virtual;
8+
use crate::FixedUpdate;
9+
10+
#[derive(Resource, Debug, Copy, Clone, Reflect, FromReflect)]
11+
#[reflect(Resource)]
12+
pub struct Fixed {
13+
period: Duration,
14+
accumulated: Duration,
15+
}
16+
17+
impl Time<Fixed> {
18+
pub fn period(&self) -> Duration {
19+
self.context().period
20+
}
21+
22+
pub fn set_period(&mut self, period: Duration) {
23+
assert_ne!(
24+
period,
25+
Duration::ZERO,
26+
"attempted to set fixed time period to zero"
27+
);
28+
self.context_mut().period = period;
29+
}
30+
31+
fn expend(&mut self) -> bool {
32+
let period = self.period();
33+
if let Some(new_value) = self.context_mut().accumulated.checked_sub(period) {
34+
// reduce accumulated and increase elapsed by period
35+
self.context_mut().accumulated = new_value;
36+
self.advance_by(period);
37+
true
38+
} else {
39+
// no more periods left in accumulated
40+
false
41+
}
42+
}
43+
}
44+
45+
impl Default for Fixed {
46+
fn default() -> Self {
47+
Self {
48+
period: Duration::from_secs_f64(1. / 60.),
49+
accumulated: Duration::ZERO,
50+
}
51+
}
52+
}
53+
54+
pub fn run_fixed_update_schedule(world: &mut World) {
55+
let delta = world.resource::<Time<Virtual>>().delta();
56+
world
57+
.resource_mut::<Time<Fixed>>()
58+
.context_mut()
59+
.accumulated += delta;
60+
61+
// Run the schedule until we run out of accumulated time
62+
let _ = world.try_schedule_scope(FixedUpdate, |world, schedule| {
63+
while world.resource_mut::<Time<Fixed>>().expend() {
64+
world
65+
.resource::<Time<Fixed>>()
66+
.as_current()
67+
.clone_into(world.resource_mut::<Time>().as_mut());
68+
schedule.run(world);
69+
}
70+
});
71+
world
72+
.resource::<Time<Virtual>>()
73+
.as_current()
74+
.clone_into(world.resource_mut::<Time>().as_mut());
75+
}

crates/bevy_time/src/fixed_timestep.rs

Lines changed: 0 additions & 158 deletions
This file was deleted.

crates/bevy_time/src/lib.rs

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22

33
/// Common run conditions
44
pub mod common_conditions;
5-
pub mod fixed_timestep;
5+
mod fixed;
6+
mod real;
67
mod stopwatch;
78
#[allow(clippy::module_inception)]
89
mod time;
910
mod timer;
11+
mod virt;
1012

11-
use fixed_timestep::FixedTime;
1213
pub use stopwatch::*;
1314
pub use time::*;
1415
pub use timer::*;
@@ -20,13 +21,17 @@ use crossbeam_channel::{Receiver, Sender};
2021
pub mod prelude {
2122
//! The Bevy Time Prelude.
2223
#[doc(hidden)]
23-
pub use crate::{fixed_timestep::FixedTime, Time, Timer, TimerMode};
24+
pub use crate::{Time, Timer, TimerMode};
2425
}
2526

2627
use bevy_app::{prelude::*, RunFixedUpdateLoop};
2728
use bevy_ecs::prelude::*;
2829

29-
use crate::fixed_timestep::run_fixed_update_schedule;
30+
use crate::fixed::run_fixed_update_schedule;
31+
use crate::fixed::Fixed;
32+
use crate::real::Real;
33+
use crate::virt::virtual_time_system;
34+
use crate::virt::Virtual;
3035

3136
/// Adds time functionality to Apps.
3237
#[derive(Default)]
@@ -40,12 +45,20 @@ pub struct TimeSystem;
4045
impl Plugin for TimePlugin {
4146
fn build(&self, app: &mut App) {
4247
app.init_resource::<Time>()
48+
.init_resource::<Time<Real>>()
49+
.init_resource::<Time<Virtual>>()
50+
.init_resource::<Time<Fixed>>()
4351
.init_resource::<TimeUpdateStrategy>()
44-
.register_type::<Timer>()
4552
.register_type::<Time>()
53+
.register_type::<Time<Real>>()
54+
.register_type::<Time<Virtual>>()
55+
.register_type::<Time<Fixed>>()
56+
.register_type::<Timer>()
4657
.register_type::<Stopwatch>()
47-
.init_resource::<FixedTime>()
48-
.add_systems(First, time_system.in_set(TimeSystem))
58+
.add_systems(
59+
First,
60+
(time_system, virtual_time_system.after(time_system)).in_set(TimeSystem),
61+
)
4962
.add_systems(RunFixedUpdateLoop, run_fixed_update_schedule);
5063

5164
#[cfg(feature = "bevy_ci_testing")]
@@ -96,7 +109,7 @@ pub fn create_time_channels() -> (TimeSender, TimeReceiver) {
96109
/// The system used to update the [`Time`] used by app logic. If there is a render world the time is sent from
97110
/// there to this system through channels. Otherwise the time is updated in this system.
98111
fn time_system(
99-
mut time: ResMut<Time>,
112+
mut time: ResMut<Time<Real>>,
100113
update_strategy: Res<TimeUpdateStrategy>,
101114
time_recv: Option<Res<TimeReceiver>>,
102115
mut has_received_time: Local<bool>,
@@ -119,9 +132,6 @@ fn time_system(
119132
match update_strategy.as_ref() {
120133
TimeUpdateStrategy::Automatic => time.update_with_instant(new_time),
121134
TimeUpdateStrategy::ManualInstant(instant) => time.update_with_instant(*instant),
122-
TimeUpdateStrategy::ManualDuration(duration) => {
123-
let last_update = time.last_update().unwrap_or_else(|| time.startup());
124-
time.update_with_instant(last_update + *duration);
125-
}
135+
TimeUpdateStrategy::ManualDuration(duration) => time.update_with_duration(*duration),
126136
}
127137
}

0 commit comments

Comments
 (0)