mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Add First/Pre/Post/Last schedules to the Fixed timestep (#10977)
Fixes https://github.com/bevyengine/bevy/issues/10974 # Objective Duplicate the ordering logic of the `Main` schedule into the `FixedMain` schedule. --- ## Changelog - `FixedUpdate` is no longer the main schedule ran in `RunFixedUpdateLoop`, `FixedMain` has replaced this and has a similar structure to `Main`. ## Migration Guide - Usage of `RunFixedUpdateLoop` should be renamed to `RunFixedMainLoop`.
This commit is contained in:
parent
a4e0a0c0b9
commit
fe28e0ec32
5 changed files with 129 additions and 36 deletions
|
@ -24,8 +24,9 @@ pub mod prelude {
|
|||
pub use crate::{
|
||||
app::App,
|
||||
main_schedule::{
|
||||
First, FixedUpdate, Last, Main, PostStartup, PostUpdate, PreStartup, PreUpdate,
|
||||
SpawnScene, Startup, StateTransition, Update,
|
||||
First, FixedFirst, FixedLast, FixedPostUpdate, FixedPreUpdate, FixedUpdate, Last, Main,
|
||||
PostStartup, PostUpdate, PreStartup, PreUpdate, SpawnScene, Startup, StateTransition,
|
||||
Update,
|
||||
},
|
||||
DynamicPlugin, Plugin, PluginGroup,
|
||||
};
|
||||
|
|
|
@ -18,8 +18,8 @@ use bevy_ecs::{
|
|||
/// * [`First`]
|
||||
/// * [`PreUpdate`]
|
||||
/// * [`StateTransition`]
|
||||
/// * [`RunFixedUpdateLoop`]
|
||||
/// * This will run [`FixedUpdate`] zero to many times, based on how much time has elapsed.
|
||||
/// * [`RunFixedMainLoop`]
|
||||
/// * This will run [`FixedMain`] zero to many times, based on how much time has elapsed.
|
||||
/// * [`Update`]
|
||||
/// * [`PostUpdate`]
|
||||
/// * [`Last`]
|
||||
|
@ -67,25 +67,62 @@ pub struct PreUpdate;
|
|||
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct StateTransition;
|
||||
|
||||
/// Runs the [`FixedUpdate`] schedule in a loop according until all relevant elapsed time has been "consumed".
|
||||
/// Runs the [`FixedMain`] schedule in a loop according until all relevant elapsed time has been "consumed".
|
||||
///
|
||||
/// See the [`Main`] schedule for some details about how schedules are run.
|
||||
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct RunFixedUpdateLoop;
|
||||
pub struct RunFixedMainLoop;
|
||||
|
||||
/// Runs first in the [`FixedMain`] schedule.
|
||||
///
|
||||
/// See the [`FixedMain`] schedule for details on how fixed updates work.
|
||||
/// See the [`Main`] schedule for some details about how schedules are run.
|
||||
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct FixedFirst;
|
||||
|
||||
/// The schedule that contains logic that must run before [`FixedUpdate`].
|
||||
///
|
||||
/// See the [`FixedMain`] schedule for details on how fixed updates work.
|
||||
/// See the [`Main`] schedule for some details about how schedules are run.
|
||||
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct FixedPreUpdate;
|
||||
|
||||
/// The schedule that contains most gameplay logic.
|
||||
///
|
||||
/// See the [`FixedMain`] schedule for details on how fixed updates work.
|
||||
/// See the [`Main`] schedule for some details about how schedules are run.
|
||||
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct FixedUpdate;
|
||||
|
||||
/// The schedule that runs after the [`FixedUpdate`] schedule, for reacting
|
||||
/// to changes made in the main update logic.
|
||||
///
|
||||
/// See the [`FixedMain`] schedule for details on how fixed updates work.
|
||||
/// See the [`Main`] schedule for some details about how schedules are run.
|
||||
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct FixedPostUpdate;
|
||||
|
||||
/// The schedule that runs last in [`FixedMain`]
|
||||
///
|
||||
/// See the [`FixedMain`] schedule for details on how fixed updates work.
|
||||
/// See the [`Main`] schedule for some details about how schedules are run.
|
||||
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct FixedLast;
|
||||
|
||||
/// The schedule that contains systems which only run after a fixed period of time has elapsed.
|
||||
///
|
||||
/// The exclusive `run_fixed_update_schedule` system runs this schedule.
|
||||
/// This is run by the [`RunFixedUpdateLoop`] schedule.
|
||||
/// The exclusive `run_fixed_main_schedule` system runs this schedule.
|
||||
/// This is run by the [`RunFixedMainLoop`] schedule.
|
||||
///
|
||||
/// Frequency of execution is configured by inserting `Time<Fixed>` resource, 64 Hz by default.
|
||||
/// See [this example](https://github.com/bevyengine/bevy/blob/latest/examples/time/time.rs).
|
||||
///
|
||||
/// See the [`Main`] schedule for some details about how schedules are run.
|
||||
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct FixedUpdate;
|
||||
pub struct FixedMain;
|
||||
|
||||
/// The schedule that contains app logic.
|
||||
/// The schedule that contains app logic. Ideally containing anything that must run once per
|
||||
/// render frame, such as UI.
|
||||
///
|
||||
/// See the [`Main`] schedule for some details about how schedules are run.
|
||||
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
|
@ -131,7 +168,7 @@ impl Default for MainScheduleOrder {
|
|||
First.intern(),
|
||||
PreUpdate.intern(),
|
||||
StateTransition.intern(),
|
||||
RunFixedUpdateLoop.intern(),
|
||||
RunFixedMainLoop.intern(),
|
||||
Update.intern(),
|
||||
SpawnScene.intern(),
|
||||
PostUpdate.intern(),
|
||||
|
@ -188,7 +225,7 @@ impl Main {
|
|||
}
|
||||
}
|
||||
|
||||
/// Initializes the [`Main`] schedule, sub schedules, and resources for a given [`App`].
|
||||
/// Initializes the [`Main`] schedule, sub schedules, and resources for a given [`App`].
|
||||
pub struct MainSchedulePlugin;
|
||||
|
||||
impl Plugin for MainSchedulePlugin {
|
||||
|
@ -196,12 +233,62 @@ impl Plugin for MainSchedulePlugin {
|
|||
// simple "facilitator" schedules benefit from simpler single threaded scheduling
|
||||
let mut main_schedule = Schedule::new(Main);
|
||||
main_schedule.set_executor_kind(ExecutorKind::SingleThreaded);
|
||||
let mut fixed_update_loop_schedule = Schedule::new(RunFixedUpdateLoop);
|
||||
fixed_update_loop_schedule.set_executor_kind(ExecutorKind::SingleThreaded);
|
||||
let mut fixed_main_schedule = Schedule::new(FixedMain);
|
||||
fixed_main_schedule.set_executor_kind(ExecutorKind::SingleThreaded);
|
||||
let mut fixed_main_loop_schedule = Schedule::new(RunFixedMainLoop);
|
||||
fixed_main_loop_schedule.set_executor_kind(ExecutorKind::SingleThreaded);
|
||||
|
||||
app.add_schedule(main_schedule)
|
||||
.add_schedule(fixed_update_loop_schedule)
|
||||
.add_schedule(fixed_main_schedule)
|
||||
.add_schedule(fixed_main_loop_schedule)
|
||||
.init_resource::<MainScheduleOrder>()
|
||||
.add_systems(Main, Main::run_main);
|
||||
.init_resource::<FixedMainScheduleOrder>()
|
||||
.add_systems(Main, Main::run_main)
|
||||
.add_systems(FixedMain, FixedMain::run_fixed_main);
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines the schedules to be run for the [`FixedMain`] schedule, including
|
||||
/// their order.
|
||||
#[derive(Resource, Debug)]
|
||||
pub struct FixedMainScheduleOrder {
|
||||
/// The labels to run for the [`FixedMain`] schedule (in the order they will be run).
|
||||
pub labels: Vec<InternedScheduleLabel>,
|
||||
}
|
||||
|
||||
impl Default for FixedMainScheduleOrder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
labels: vec![
|
||||
FixedFirst.intern(),
|
||||
FixedPreUpdate.intern(),
|
||||
FixedUpdate.intern(),
|
||||
FixedPostUpdate.intern(),
|
||||
FixedLast.intern(),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FixedMainScheduleOrder {
|
||||
/// Adds the given `schedule` after the `after` schedule
|
||||
pub fn insert_after(&mut self, after: impl ScheduleLabel, schedule: impl ScheduleLabel) {
|
||||
let index = self
|
||||
.labels
|
||||
.iter()
|
||||
.position(|current| (**current).eq(&after))
|
||||
.unwrap_or_else(|| panic!("Expected {after:?} to exist"));
|
||||
self.labels.insert(index + 1, schedule.intern());
|
||||
}
|
||||
}
|
||||
|
||||
impl FixedMain {
|
||||
/// A system that runs the fixed timestep's "main schedule"
|
||||
pub fn run_fixed_main(world: &mut World) {
|
||||
world.resource_scope(|world, order: Mut<FixedMainScheduleOrder>| {
|
||||
for &label in &order.labels {
|
||||
let _ = world.try_run_schedule(label);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use bevy_app::FixedMain;
|
||||
use bevy_ecs::world::World;
|
||||
use bevy_reflect::Reflect;
|
||||
use bevy_utils::Duration;
|
||||
|
||||
use crate::{time::Time, virt::Virtual, FixedUpdate};
|
||||
use crate::{time::Time, virt::Virtual};
|
||||
|
||||
/// The fixed timestep game clock following virtual time.
|
||||
///
|
||||
|
@ -12,7 +13,8 @@ use crate::{time::Time, virt::Virtual, FixedUpdate};
|
|||
/// It is automatically inserted as a resource by
|
||||
/// [`TimePlugin`](crate::TimePlugin) and updated based on
|
||||
/// [`Time<Virtual>`](Virtual). The fixed clock is automatically set as the
|
||||
/// generic [`Time`] resource during [`FixedUpdate`] schedule processing.
|
||||
/// generic [`Time`] resource during [`FixedUpdate`](bevy_app::FixedUpdate)
|
||||
/// schedule processing.
|
||||
///
|
||||
/// The fixed timestep clock advances in fixed-size increments, which is
|
||||
/// extremely useful for writing logic (like physics) that should have
|
||||
|
@ -26,7 +28,9 @@ use crate::{time::Time, virt::Virtual, FixedUpdate};
|
|||
/// frame). Additionally, the value is a power of two which losslessly converts
|
||||
/// into [`f32`] and [`f64`].
|
||||
///
|
||||
/// To run a system on a fixed timestep, add it to the [`FixedUpdate`] schedule.
|
||||
/// To run a system on a fixed timestep, add it to one of the [`FixedMain`]
|
||||
/// schedules, most commonly [`FixedUpdate`](bevy_app::FixedUpdate).
|
||||
///
|
||||
/// This schedule is run a number of times between
|
||||
/// [`PreUpdate`](bevy_app::PreUpdate) and [`Update`](bevy_app::Update)
|
||||
/// according to the accumulated [`overstep()`](Time::overstep) time divided by
|
||||
|
@ -43,20 +47,21 @@ use crate::{time::Time, virt::Virtual, FixedUpdate};
|
|||
/// means it is affected by [`pause()`](Time::pause),
|
||||
/// [`set_relative_speed()`](Time::set_relative_speed) and
|
||||
/// [`set_max_delta()`](Time::set_max_delta) from virtual time. If the virtual
|
||||
/// clock is paused, the [`FixedUpdate`] schedule will not run. It is guaranteed
|
||||
/// that the [`elapsed()`](Time::elapsed) time in `Time<Fixed>` is always
|
||||
/// between the previous `elapsed()` and the current `elapsed()` value in
|
||||
/// `Time<Virtual>`, so the values are compatible.
|
||||
/// clock is paused, the [`FixedUpdate`](bevy_app::FixedUpdate) schedule will
|
||||
/// not run. It is guaranteed that the [`elapsed()`](Time::elapsed) time in
|
||||
/// `Time<Fixed>` is always between the previous `elapsed()` and the current
|
||||
/// `elapsed()` value in `Time<Virtual>`, so the values are compatible.
|
||||
///
|
||||
/// Changing the timestep size while the game is running should not normally be
|
||||
/// done, as having a regular interval is the point of this schedule, but it may
|
||||
/// be necessary for effects like "bullet-time" if the normal granularity of the
|
||||
/// fixed timestep is too big for the slowed down time. In this case,
|
||||
/// [`set_timestep()`](Time::set_timestep) and be called to set a new value. The
|
||||
/// new value will be used immediately for the next run of the [`FixedUpdate`]
|
||||
/// schedule, meaning that it will affect the [`delta()`](Time::delta) value for
|
||||
/// the very next [`FixedUpdate`], even if it is still during the same frame.
|
||||
/// Any [`overstep()`](Time::overstep) present in the accumulator will be
|
||||
/// new value will be used immediately for the next run of the
|
||||
/// [`FixedUpdate`](bevy_app::FixedUpdate) schedule, meaning that it will affect
|
||||
/// the [`delta()`](Time::delta) value for the very next
|
||||
/// [`FixedUpdate`](bevy_app::FixedUpdate), even if it is still during the same
|
||||
/// frame. Any [`overstep()`](Time::overstep) present in the accumulator will be
|
||||
/// processed according to the new [`timestep()`](Time::timestep) value.
|
||||
#[derive(Debug, Copy, Clone, Reflect)]
|
||||
pub struct Fixed {
|
||||
|
@ -225,14 +230,14 @@ impl Default for Fixed {
|
|||
}
|
||||
}
|
||||
|
||||
/// Runs [`FixedUpdate`] zero or more times based on delta of
|
||||
/// Runs [`FixedMain`] zero or more times based on delta of
|
||||
/// [`Time<Virtual>`](Virtual) and [`Time::overstep`]
|
||||
pub fn run_fixed_update_schedule(world: &mut World) {
|
||||
pub fn run_fixed_main_schedule(world: &mut World) {
|
||||
let delta = world.resource::<Time<Virtual>>().delta();
|
||||
world.resource_mut::<Time<Fixed>>().accumulate(delta);
|
||||
|
||||
// Run the schedule until we run out of accumulated time
|
||||
let _ = world.try_schedule_scope(FixedUpdate, |world, schedule| {
|
||||
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);
|
||||
|
|
|
@ -24,7 +24,7 @@ pub mod prelude {
|
|||
pub use crate::{Fixed, Real, Time, Timer, TimerMode, Virtual};
|
||||
}
|
||||
|
||||
use bevy_app::{prelude::*, RunFixedUpdateLoop};
|
||||
use bevy_app::{prelude::*, RunFixedMainLoop};
|
||||
use bevy_ecs::event::{event_queue_update_system, EventUpdateSignal};
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_utils::{tracing::warn, Duration, Instant};
|
||||
|
@ -57,11 +57,11 @@ impl Plugin for TimePlugin {
|
|||
First,
|
||||
(time_system, virtual_time_system.after(time_system)).in_set(TimeSystem),
|
||||
)
|
||||
.add_systems(RunFixedUpdateLoop, run_fixed_update_schedule);
|
||||
.add_systems(RunFixedMainLoop, run_fixed_main_schedule);
|
||||
|
||||
// ensure the events are not dropped until `FixedUpdate` systems can observe them
|
||||
// ensure the events are not dropped until `FixedMain` systems can observe them
|
||||
app.init_resource::<EventUpdateSignal>()
|
||||
.add_systems(FixedUpdate, event_queue_update_system);
|
||||
.add_systems(FixedPostUpdate, event_queue_update_system);
|
||||
|
||||
#[cfg(feature = "bevy_ci_testing")]
|
||||
if let Some(ci_testing_config) = app
|
||||
|
|
|
@ -15,7 +15,7 @@ use bevy_utils::Duration;
|
|||
/// virtual time.
|
||||
/// - [`Time`] is a generic clock that corresponds to "current" or "default"
|
||||
/// time for systems. It contains [`Time<Virtual>`](crate::virt::Virtual)
|
||||
/// except inside the [`FixedUpdate`](bevy_app::FixedUpdate) schedule when it
|
||||
/// except inside the [`FixedMain`](bevy_app::FixedMain) schedule when it
|
||||
/// contains [`Time<Fixed>`](crate::fixed::Fixed).
|
||||
///
|
||||
/// The time elapsed since the previous time this clock was advanced is saved as
|
||||
|
@ -45,7 +45,7 @@ use bevy_utils::Duration;
|
|||
/// [`elapsed()`](Time::elapsed) should use `Res<Time>` to access the default
|
||||
/// time configured for the program. By default, this refers to
|
||||
/// [`Time<Virtual>`](crate::virt::Virtual) except during the
|
||||
/// [`FixedUpdate`](bevy_app::FixedUpdate) schedule when it refers to
|
||||
/// [`FixedMain`](bevy_app::FixedMain) schedule when it refers to
|
||||
/// [`Time<Fixed>`](crate::fixed::Fixed). This ensures your system can be used
|
||||
/// either in [`Update`](bevy_app::Update) or
|
||||
/// [`FixedUpdate`](bevy_app::FixedUpdate) schedule depending on what is needed.
|
||||
|
|
Loading…
Reference in a new issue