use bevy_app::FixedMain;
use bevy_ecs::world::World;
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
use core::time::Duration;
use crate::{time::Time, virt::Virtual};
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Clone))]
pub struct Fixed {
timestep: Duration,
overstep: Duration,
}
impl Time<Fixed> {
const DEFAULT_TIMESTEP: Duration = Duration::from_micros(15625);
pub fn from_duration(timestep: Duration) -> Self {
let mut ret = Self::default();
ret.set_timestep(timestep);
ret
}
pub fn from_seconds(seconds: f64) -> Self {
let mut ret = Self::default();
ret.set_timestep_seconds(seconds);
ret
}
pub fn from_hz(hz: f64) -> Self {
let mut ret = Self::default();
ret.set_timestep_hz(hz);
ret
}
#[inline]
pub fn timestep(&self) -> Duration {
self.context().timestep
}
#[inline]
pub fn set_timestep(&mut self, timestep: Duration) {
assert_ne!(
timestep,
Duration::ZERO,
"attempted to set fixed timestep to zero"
);
self.context_mut().timestep = timestep;
}
#[inline]
pub fn set_timestep_seconds(&mut self, seconds: f64) {
assert!(
seconds.is_sign_positive(),
"seconds less than or equal to zero"
);
assert!(seconds.is_finite(), "seconds is infinite");
self.set_timestep(Duration::from_secs_f64(seconds));
}
#[inline]
pub fn set_timestep_hz(&mut self, hz: f64) {
assert!(hz.is_sign_positive(), "Hz less than or equal to zero");
assert!(hz.is_finite(), "Hz is infinite");
self.set_timestep_seconds(1.0 / hz);
}
#[inline]
pub fn overstep(&self) -> Duration {
self.context().overstep
}
#[inline]
pub fn accumulate_overstep(&mut self, delta: Duration) {
self.context_mut().overstep += delta;
}
#[inline]
pub fn discard_overstep(&mut self, discard: Duration) {
let context = self.context_mut();
context.overstep = context.overstep.saturating_sub(discard);
}
#[inline]
pub fn overstep_fraction(&self) -> f32 {
self.context().overstep.as_secs_f32() / self.context().timestep.as_secs_f32()
}
#[inline]
pub fn overstep_fraction_f64(&self) -> f64 {
self.context().overstep.as_secs_f64() / self.context().timestep.as_secs_f64()
}
fn expend(&mut self) -> bool {
let timestep = self.timestep();
if let Some(new_value) = self.context_mut().overstep.checked_sub(timestep) {
self.context_mut().overstep = new_value;
self.advance_by(timestep);
true
} else {
false
}
}
}
impl Default for Fixed {
fn default() -> Self {
Self {
timestep: Time::<Fixed>::DEFAULT_TIMESTEP,
overstep: Duration::ZERO,
}
}
}
pub fn run_fixed_main_schedule(world: &mut World) {
let delta = world.resource::<Time<Virtual>>().delta();
world
.resource_mut::<Time<Fixed>>()
.accumulate_overstep(delta);
let _ = world.try_schedule_scope(FixedMain, |world, schedule| {
while world.resource_mut::<Time<Fixed>>().expend() {
*world.resource_mut::<Time>() = world.resource::<Time<Fixed>>().as_generic();
schedule.run(world);
}
});
*world.resource_mut::<Time>() = world.resource::<Time<Virtual>>().as_generic();
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_set_timestep() {
let mut time = Time::<Fixed>::default();
assert_eq!(time.timestep(), Time::<Fixed>::DEFAULT_TIMESTEP);
time.set_timestep(Duration::from_millis(500));
assert_eq!(time.timestep(), Duration::from_millis(500));
time.set_timestep_seconds(0.25);
assert_eq!(time.timestep(), Duration::from_millis(250));
time.set_timestep_hz(8.0);
assert_eq!(time.timestep(), Duration::from_millis(125));
}
#[test]
fn test_expend() {
let mut time = Time::<Fixed>::from_seconds(2.0);
assert_eq!(time.delta(), Duration::ZERO);
assert_eq!(time.elapsed(), Duration::ZERO);
time.accumulate_overstep(Duration::from_secs(1));
assert_eq!(time.delta(), Duration::ZERO);
assert_eq!(time.elapsed(), Duration::ZERO);
assert_eq!(time.overstep(), Duration::from_secs(1));
assert_eq!(time.overstep_fraction(), 0.5);
assert_eq!(time.overstep_fraction_f64(), 0.5);
assert!(!time.expend());
assert_eq!(time.delta(), Duration::ZERO);
assert_eq!(time.elapsed(), Duration::ZERO);
assert_eq!(time.overstep(), Duration::from_secs(1));
assert_eq!(time.overstep_fraction(), 0.5);
assert_eq!(time.overstep_fraction_f64(), 0.5);
time.accumulate_overstep(Duration::from_secs(1));
assert_eq!(time.delta(), Duration::ZERO);
assert_eq!(time.elapsed(), Duration::ZERO);
assert_eq!(time.overstep(), Duration::from_secs(2));
assert_eq!(time.overstep_fraction(), 1.0);
assert_eq!(time.overstep_fraction_f64(), 1.0);
assert!(time.expend());
assert_eq!(time.delta(), Duration::from_secs(2));
assert_eq!(time.elapsed(), Duration::from_secs(2));
assert_eq!(time.overstep(), Duration::ZERO);
assert_eq!(time.overstep_fraction(), 0.0);
assert_eq!(time.overstep_fraction_f64(), 0.0);
assert!(!time.expend());
assert_eq!(time.delta(), Duration::from_secs(2));
assert_eq!(time.elapsed(), Duration::from_secs(2));
assert_eq!(time.overstep(), Duration::ZERO);
assert_eq!(time.overstep_fraction(), 0.0);
assert_eq!(time.overstep_fraction_f64(), 0.0);
time.accumulate_overstep(Duration::from_secs(1));
assert_eq!(time.delta(), Duration::from_secs(2));
assert_eq!(time.elapsed(), Duration::from_secs(2));
assert_eq!(time.overstep(), Duration::from_secs(1));
assert_eq!(time.overstep_fraction(), 0.5);
assert_eq!(time.overstep_fraction_f64(), 0.5);
assert!(!time.expend());
assert_eq!(time.delta(), Duration::from_secs(2));
assert_eq!(time.elapsed(), Duration::from_secs(2));
assert_eq!(time.overstep(), Duration::from_secs(1));
assert_eq!(time.overstep_fraction(), 0.5);
assert_eq!(time.overstep_fraction_f64(), 0.5);
}
#[test]
fn test_expend_multiple() {
let mut time = Time::<Fixed>::from_seconds(2.0);
time.accumulate_overstep(Duration::from_secs(7));
assert_eq!(time.overstep(), Duration::from_secs(7));
assert!(time.expend()); assert_eq!(time.elapsed(), Duration::from_secs(2));
assert_eq!(time.overstep(), Duration::from_secs(5));
assert!(time.expend()); assert_eq!(time.elapsed(), Duration::from_secs(4));
assert_eq!(time.overstep(), Duration::from_secs(3));
assert!(time.expend()); assert_eq!(time.elapsed(), Duration::from_secs(6));
assert_eq!(time.overstep(), Duration::from_secs(1));
assert!(!time.expend()); assert_eq!(time.elapsed(), Duration::from_secs(6));
assert_eq!(time.overstep(), Duration::from_secs(1));
}
}