mod time;
use dynamics::solver::schedule::SubstepCount;
pub use time::*;
use core::time::Duration;
#[allow(unused_imports)]
use crate::prelude::*;
use bevy::{
ecs::{
change_detection::Tick,
intern::Interned,
schedule::{ExecutorKind, LogLevel, ScheduleBuildSettings, ScheduleLabel},
system::SystemChangeTick,
},
prelude::*,
transform::TransformSystems,
};
pub struct PhysicsSchedulePlugin {
schedule: Interned<dyn ScheduleLabel>,
}
impl PhysicsSchedulePlugin {
pub fn new(schedule: impl ScheduleLabel) -> Self {
Self {
schedule: schedule.intern(),
}
}
}
impl Default for PhysicsSchedulePlugin {
fn default() -> Self {
Self::new(FixedPostUpdate)
}
}
impl Plugin for PhysicsSchedulePlugin {
fn build(&self, app: &mut App) {
app.register_type::<Time<Physics>>();
app.init_resource::<Time<Physics>>()
.insert_resource(Time::new_with(Substeps))
.init_resource::<SubstepCount>()
.init_resource::<LastPhysicsTick>();
app.init_resource::<PhysicsLengthUnit>();
let schedule = self.schedule;
app.configure_sets(
schedule,
(
PhysicsSystems::First,
PhysicsSystems::Prepare,
PhysicsSystems::StepSimulation,
PhysicsSystems::Writeback,
PhysicsSystems::Last,
)
.chain()
.before(TransformSystems::Propagate),
);
app.edit_schedule(PhysicsSchedule, |schedule| {
schedule
.set_executor_kind(ExecutorKind::SingleThreaded)
.set_build_settings(ScheduleBuildSettings {
ambiguity_detection: LogLevel::Error,
..default()
});
schedule.configure_sets(
(
PhysicsStepSystems::First,
PhysicsStepSystems::BroadPhase,
PhysicsStepSystems::NarrowPhase,
PhysicsStepSystems::Solver,
PhysicsStepSystems::Sleeping,
PhysicsStepSystems::SpatialQuery,
PhysicsStepSystems::Finalize,
PhysicsStepSystems::Last,
)
.chain(),
);
});
app.add_systems(
schedule,
run_physics_schedule.in_set(PhysicsSystems::StepSimulation),
);
app.add_systems(
PhysicsSchedule,
update_last_physics_tick.after(PhysicsStepSystems::Last),
);
#[cfg(debug_assertions)]
app.add_systems(
schedule,
assert_components_finite.in_set(PhysicsSystems::First),
);
}
}
struct IsFirstRun(bool);
impl Default for IsFirstRun {
fn default() -> Self {
Self(true)
}
}
#[derive(Debug, Hash, PartialEq, Eq, Clone, ScheduleLabel)]
pub struct PhysicsSchedule;
#[derive(SystemSet, Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum PhysicsSystems {
First,
Prepare,
StepSimulation,
Writeback,
Last,
}
#[deprecated(since = "0.4.0", note = "Renamed to `PhysicsSystems`")]
pub type PhysicsSet = PhysicsSystems;
#[derive(SystemSet, Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum PhysicsStepSystems {
First,
BroadPhase,
NarrowPhase,
Solver,
Sleeping,
SpatialQuery,
Finalize,
Last,
}
#[deprecated(since = "0.4.0", note = "Renamed to `PhysicsStepSystems`")]
pub type PhysicsStepSet = PhysicsStepSystems;
#[derive(Resource, Reflect, Default)]
#[reflect(Resource, Default)]
pub struct LastPhysicsTick(pub Tick);
pub(crate) fn is_changed_after_tick<C: Component>(
component_ref: Ref<C>,
tick: Tick,
this_run: Tick,
) -> bool {
let last_changed = component_ref.last_changed();
component_ref.is_changed() && last_changed.is_newer_than(tick, this_run)
}
fn run_physics_schedule(world: &mut World, mut is_first_run: Local<IsFirstRun>) {
let _ = world.try_schedule_scope(PhysicsSchedule, |world, schedule| {
let is_paused = world.resource::<Time<Physics>>().is_paused();
let old_clock = world.resource::<Time>().as_generic();
let physics_clock = world.resource_mut::<Time<Physics>>();
let timestep = old_clock
.delta()
.mul_f64(physics_clock.relative_speed_f64());
if !is_paused {
world.resource_mut::<Time<Physics>>().advance_by(timestep);
let SubstepCount(substeps) = *world.resource::<SubstepCount>();
let sub_delta = timestep.div_f64(substeps as f64);
world.resource_mut::<Time<Substeps>>().advance_by(sub_delta);
}
*world.resource_mut::<Time>() = world.resource::<Time<Physics>>().as_generic();
if !world.resource::<Time>().delta().is_zero() {
trace!("running PhysicsSchedule");
schedule.run(world);
}
if is_paused {
world
.resource_mut::<Time<Physics>>()
.advance_by(Duration::ZERO);
}
*world.resource_mut::<Time>() = old_clock;
});
is_first_run.0 = false;
}
fn update_last_physics_tick(
mut last_physics_tick: ResMut<LastPhysicsTick>,
system_change_tick: SystemChangeTick,
) {
last_physics_tick.0 = system_change_tick.this_run();
}
#[cfg(debug_assertions)]
fn assert_components_finite(
pos_query: Query<(Entity, Ref<Position>)>,
lin_vel_query: Query<(Entity, Ref<LinearVelocity>)>,
ang_vel_query: Query<(Entity, Ref<AngularVelocity>)>,
) {
macro_rules! assert_finite {
($ent:expr, $val:ident, $ty:ty) => {
debug_assert!(
$val.is_finite(),
"NaN or infinity found in Avian component: type={} entity={} location='{}' (enable feature \"bevy/track_location\" if location is empty)",
stringify!($ty),
$ent,
$val.changed_by()
);
};
}
for (entity, position) in pos_query {
assert_finite!(entity, position, Position);
}
for (entity, velocity) in lin_vel_query {
assert_finite!(entity, velocity, LinearVelocity);
}
for (entity, velocity) in ang_vel_query {
assert_finite!(entity, velocity, AngularVelocity);
}
}