use web_time::Duration;
use bevy_ecs::prelude::*;
use bevy_ecs::schedule::{IntoScheduleConfigs, Schedule};
use bevy_ecs::system::ScheduleSystem;
pub use bevy_ecs;
pub struct Sim {
pub world: World,
schedule: Schedule,
accumulator: Duration,
pub step: Duration,
}
#[derive(Resource, Clone, Copy, Debug, Default)]
pub struct SimTime {
pub elapsed_secs: f64,
pub delta_secs: f32,
}
impl Sim {
pub fn new(step: Duration) -> Self {
let mut world = World::new();
world.init_resource::<SimTime>();
Self {
world,
schedule: Schedule::default(),
accumulator: Duration::ZERO,
step,
}
}
pub fn with_default_step() -> Self {
Self::new(Duration::from_secs_f64(1.0 / 60.0))
}
pub fn add_system<M>(&mut self, system: impl IntoSystem<(), (), M>) -> &mut Self {
self.schedule.add_systems(system);
self
}
pub fn add_chained_systems<M>(
&mut self,
systems: impl IntoScheduleConfigs<ScheduleSystem, M>,
) -> &mut Self {
self.schedule.add_systems(systems);
self
}
pub fn step(&mut self, dt: Duration) -> u32 {
self.accumulator += dt;
let mut ran = 0;
while self.accumulator >= self.step {
self.accumulator -= self.step;
self.tick();
ran += 1;
}
ran
}
pub fn tick(&mut self) {
{
let mut time = self.world.resource_mut::<SimTime>();
time.delta_secs = self.step.as_secs_f32();
time.elapsed_secs += self.step.as_secs_f64();
}
self.schedule.run(&mut self.world);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fixed_steps_accumulate() {
let mut sim = Sim::with_default_step();
let ran = sim.step(Duration::from_nanos(50_000_001));
assert_eq!(ran, 3);
let time = sim.world.resource::<SimTime>();
assert!((time.elapsed_secs - 3.0 / 60.0).abs() < 1e-9);
}
}