1use std::time::Duration;
9
10use bevy_ecs::prelude::*;
11use bevy_ecs::schedule::{IntoScheduleConfigs, Schedule};
12use bevy_ecs::system::ScheduleSystem;
13
14pub use bevy_ecs;
15
16pub struct Sim {
18 pub world: World,
20 schedule: Schedule,
21 accumulator: Duration,
22 pub step: Duration,
24}
25
26#[derive(Resource, Clone, Copy, Debug, Default)]
28pub struct SimTime {
29 pub elapsed_secs: f64,
31 pub delta_secs: f32,
33}
34
35impl Sim {
36 pub fn new(step: Duration) -> Self {
37 let mut world = World::new();
38 world.init_resource::<SimTime>();
39 Self {
40 world,
41 schedule: Schedule::default(),
42 accumulator: Duration::ZERO,
43 step,
44 }
45 }
46
47 pub fn with_default_step() -> Self {
49 Self::new(Duration::from_secs_f64(1.0 / 60.0))
50 }
51
52 pub fn add_system<M>(&mut self, system: impl IntoSystem<(), (), M>) -> &mut Self {
54 self.schedule.add_systems(system);
55 self
56 }
57
58 pub fn add_chained_systems<M>(
65 &mut self,
66 systems: impl IntoScheduleConfigs<ScheduleSystem, M>,
67 ) -> &mut Self {
68 self.schedule.add_systems(systems);
69 self
70 }
71
72 pub fn step(&mut self, dt: Duration) -> u32 {
75 self.accumulator += dt;
76 let mut ran = 0;
77 while self.accumulator >= self.step {
78 self.accumulator -= self.step;
79 self.tick();
80 ran += 1;
81 }
82 ran
83 }
84
85 pub fn tick(&mut self) {
89 {
90 let mut time = self.world.resource_mut::<SimTime>();
91 time.delta_secs = self.step.as_secs_f32();
92 time.elapsed_secs += self.step.as_secs_f64();
93 }
94 self.schedule.run(&mut self.world);
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn fixed_steps_accumulate() {
104 let mut sim = Sim::with_default_step();
105 let ran = sim.step(Duration::from_nanos(50_000_001));
106 assert_eq!(ran, 3);
107 let time = sim.world.resource::<SimTime>();
108 assert!((time.elapsed_secs - 3.0 / 60.0).abs() < 1e-9);
109 }
110}