bevy_app/
main_schedule.rs

1use crate::{App, Plugin};
2use alloc::{vec, vec::Vec};
3use bevy_ecs::{
4    resource::Resource,
5    schedule::{
6        ExecutorKind, InternedScheduleLabel, IntoScheduleConfigs, Schedule, ScheduleLabel,
7        SystemSet,
8    },
9    system::Local,
10    world::{Mut, World},
11};
12
13/// The schedule that contains the app logic that is evaluated each tick of [`App::update()`].
14///
15/// By default, it will run the following schedules in the given order:
16///
17/// On the first run of the schedule (and only on the first run), it will run:
18/// * [`StateTransition`] [^1]
19///      * This means that [`OnEnter(MyState::Foo)`] will be called *before* [`PreStartup`]
20///        if `MyState` was added to the app with `MyState::Foo` as the initial state,
21///        as well as [`OnEnter(MyComputedState)`] if it `compute`s to `Some(Self)` in `MyState::Foo`.
22///      * If you want to run systems before any state transitions, regardless of which state is the starting state,
23///        for example, for registering required components, you can add your own custom startup schedule
24///        before [`StateTransition`]. See [`MainScheduleOrder::insert_startup_before`] for more details.
25/// * [`PreStartup`]
26/// * [`Startup`]
27/// * [`PostStartup`]
28///
29/// Then it will run:
30/// * [`First`]
31/// * [`PreUpdate`]
32/// * [`StateTransition`] [^1]
33/// * [`RunFixedMainLoop`]
34///     * This will run [`FixedMain`] zero to many times, based on how much time has elapsed.
35/// * [`Update`]
36/// * [`PostUpdate`]
37/// * [`Last`]
38///
39/// # Rendering
40///
41/// Note rendering is not executed in the main schedule by default.
42/// Instead, rendering is performed in a separate [`SubApp`]
43/// which exchanges data with the main app in between the main schedule runs.
44///
45/// See [`RenderPlugin`] and [`PipelinedRenderingPlugin`] for more details.
46///
47/// [^1]: [`StateTransition`] is inserted only if you have `bevy_state` feature enabled. It is enabled in `default` features.
48///
49/// [`StateTransition`]: https://docs.rs/bevy/latest/bevy/prelude/struct.StateTransition.html
50/// [`OnEnter(MyState::Foo)`]: https://docs.rs/bevy/latest/bevy/prelude/struct.OnEnter.html
51/// [`OnEnter(MyComputedState)`]: https://docs.rs/bevy/latest/bevy/prelude/struct.OnEnter.html
52/// [`RenderPlugin`]: https://docs.rs/bevy/latest/bevy/render/struct.RenderPlugin.html
53/// [`PipelinedRenderingPlugin`]: https://docs.rs/bevy/latest/bevy/render/pipelined_rendering/struct.PipelinedRenderingPlugin.html
54/// [`SubApp`]: crate::SubApp
55#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
56pub struct Main;
57
58/// The schedule that runs before [`Startup`].
59///
60/// See the [`Main`] schedule for some details about how schedules are run.
61#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
62pub struct PreStartup;
63
64/// The schedule that runs once when the app starts.
65///
66/// See the [`Main`] schedule for some details about how schedules are run.
67#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
68pub struct Startup;
69
70/// The schedule that runs once after [`Startup`].
71///
72/// See the [`Main`] schedule for some details about how schedules are run.
73#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
74pub struct PostStartup;
75
76/// Runs first in the schedule.
77///
78/// See the [`Main`] schedule for some details about how schedules are run.
79#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
80pub struct First;
81
82/// The schedule that contains logic that must run before [`Update`]. For example, a system that reads raw keyboard
83/// input OS events into a `Messages` resource. This enables systems in [`Update`] to consume the messages from the `Messages`
84/// resource without actually knowing about (or taking a direct scheduler dependency on) the "os-level keyboard event system".
85///
86/// [`PreUpdate`] exists to do "engine/plugin preparation work" that ensures the APIs consumed in [`Update`] are "ready".
87/// [`PreUpdate`] abstracts out "pre work implementation details".
88///
89/// See the [`Main`] schedule for some details about how schedules are run.
90#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
91pub struct PreUpdate;
92
93/// Runs the [`FixedMain`] schedule in a loop according until all relevant elapsed time has been "consumed".
94///
95/// If you need to order your variable timestep systems before or after
96/// the fixed update logic, use the [`RunFixedMainLoopSystems`] system set.
97///
98/// Note that in contrast to most other Bevy schedules, systems added directly to
99/// [`RunFixedMainLoop`] will *not* be parallelized between each other.
100///
101/// See the [`Main`] schedule for some details about how schedules are run.
102#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
103pub struct RunFixedMainLoop;
104
105/// Runs first in the [`FixedMain`] schedule.
106///
107/// See the [`FixedMain`] schedule for details on how fixed updates work.
108/// See the [`Main`] schedule for some details about how schedules are run.
109#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
110pub struct FixedFirst;
111
112/// The schedule that contains logic that must run before [`FixedUpdate`].
113///
114/// See the [`FixedMain`] schedule for details on how fixed updates work.
115/// See the [`Main`] schedule for some details about how schedules are run.
116#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
117pub struct FixedPreUpdate;
118
119/// The schedule that contains most gameplay logic, which runs at a fixed rate rather than every render frame.
120/// For logic that should run once per render frame, use the [`Update`] schedule instead.
121///
122/// Examples of systems that should run at a fixed rate include (but are not limited to):
123/// - Physics
124/// - AI
125/// - Networking
126/// - Game rules
127///
128/// See the [`Update`] schedule for examples of systems that *should not* use this schedule.
129/// See the [`FixedMain`] schedule for details on how fixed updates work.
130/// See the [`Main`] schedule for some details about how schedules are run.
131#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
132pub struct FixedUpdate;
133
134/// The schedule that runs after the [`FixedUpdate`] schedule, for reacting
135/// to changes made in the main update logic.
136///
137/// See the [`FixedMain`] schedule for details on how fixed updates work.
138/// See the [`Main`] schedule for some details about how schedules are run.
139#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
140pub struct FixedPostUpdate;
141
142/// The schedule that runs last in [`FixedMain`]
143///
144/// See the [`FixedMain`] schedule for details on how fixed updates work.
145/// See the [`Main`] schedule for some details about how schedules are run.
146#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
147pub struct FixedLast;
148
149/// The schedule that contains systems which only run after a fixed period of time has elapsed.
150///
151/// This is run by the [`RunFixedMainLoop`] schedule. If you need to order your variable timestep systems
152/// before or after the fixed update logic, use the [`RunFixedMainLoopSystems`] system set.
153///
154/// Frequency of execution is configured by inserting `Time<Fixed>` resource, 64 Hz by default.
155/// See [this example](https://github.com/bevyengine/bevy/blob/latest/examples/time/time.rs).
156///
157/// See the [`Main`] schedule for some details about how schedules are run.
158#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
159pub struct FixedMain;
160
161/// The schedule that contains any app logic that must run once per render frame.
162/// For most gameplay logic, consider using [`FixedUpdate`] instead.
163///
164/// Examples of systems that should run once per render frame include (but are not limited to):
165/// - UI
166/// - Input handling
167/// - Audio control
168///
169/// See the [`FixedUpdate`] schedule for examples of systems that *should not* use this schedule.
170/// See the [`Main`] schedule for some details about how schedules are run.
171#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
172pub struct Update;
173
174/// The schedule that contains scene spawning.
175///
176/// See the [`Main`] schedule for some details about how schedules are run.
177#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
178pub struct SpawnScene;
179
180/// The schedule that contains logic that must run after [`Update`]. For example, synchronizing "local transforms" in a hierarchy
181/// to "global" absolute transforms. This enables the [`PostUpdate`] transform-sync system to react to "local transform" changes in
182/// [`Update`] without the [`Update`] systems needing to know about (or add scheduler dependencies for) the "global transform sync system".
183///
184/// [`PostUpdate`] exists to do "engine/plugin response work" to things that happened in [`Update`].
185/// [`PostUpdate`] abstracts out "implementation details" from users defining systems in [`Update`].
186///
187/// See the [`Main`] schedule for some details about how schedules are run.
188#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
189pub struct PostUpdate;
190
191/// Runs last in the schedule.
192///
193/// See the [`Main`] schedule for some details about how schedules are run.
194#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
195pub struct Last;
196
197/// Animation system set. This exists in [`PostUpdate`].
198#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
199pub struct AnimationSystems;
200
201/// Deprecated alias for [`AnimationSystems`].
202#[deprecated(since = "0.17.0", note = "Renamed to `AnimationSystems`.")]
203pub type Animation = AnimationSystems;
204
205/// Defines the schedules to be run for the [`Main`] schedule, including
206/// their order.
207#[derive(Resource, Debug)]
208pub struct MainScheduleOrder {
209    /// The labels to run for the main phase of the [`Main`] schedule (in the order they will be run).
210    pub labels: Vec<InternedScheduleLabel>,
211    /// The labels to run for the startup phase of the [`Main`] schedule (in the order they will be run).
212    pub startup_labels: Vec<InternedScheduleLabel>,
213}
214
215impl Default for MainScheduleOrder {
216    fn default() -> Self {
217        Self {
218            labels: vec![
219                First.intern(),
220                PreUpdate.intern(),
221                RunFixedMainLoop.intern(),
222                Update.intern(),
223                SpawnScene.intern(),
224                PostUpdate.intern(),
225                Last.intern(),
226            ],
227            startup_labels: vec![PreStartup.intern(), Startup.intern(), PostStartup.intern()],
228        }
229    }
230}
231
232impl MainScheduleOrder {
233    /// Adds the given `schedule` after the `after` schedule in the main list of schedules.
234    pub fn insert_after(&mut self, after: impl ScheduleLabel, schedule: impl ScheduleLabel) {
235        let index = self
236            .labels
237            .iter()
238            .position(|current| (**current).eq(&after))
239            .unwrap_or_else(|| panic!("Expected {after:?} to exist"));
240        self.labels.insert(index + 1, schedule.intern());
241    }
242
243    /// Adds the given `schedule` before the `before` schedule in the main list of schedules.
244    pub fn insert_before(&mut self, before: impl ScheduleLabel, schedule: impl ScheduleLabel) {
245        let index = self
246            .labels
247            .iter()
248            .position(|current| (**current).eq(&before))
249            .unwrap_or_else(|| panic!("Expected {before:?} to exist"));
250        self.labels.insert(index, schedule.intern());
251    }
252
253    /// Adds the given `schedule` after the `after` schedule in the list of startup schedules.
254    pub fn insert_startup_after(
255        &mut self,
256        after: impl ScheduleLabel,
257        schedule: impl ScheduleLabel,
258    ) {
259        let index = self
260            .startup_labels
261            .iter()
262            .position(|current| (**current).eq(&after))
263            .unwrap_or_else(|| panic!("Expected {after:?} to exist"));
264        self.startup_labels.insert(index + 1, schedule.intern());
265    }
266
267    /// Adds the given `schedule` before the `before` schedule in the list of startup schedules.
268    pub fn insert_startup_before(
269        &mut self,
270        before: impl ScheduleLabel,
271        schedule: impl ScheduleLabel,
272    ) {
273        let index = self
274            .startup_labels
275            .iter()
276            .position(|current| (**current).eq(&before))
277            .unwrap_or_else(|| panic!("Expected {before:?} to exist"));
278        self.startup_labels.insert(index, schedule.intern());
279    }
280}
281
282impl Main {
283    /// A system that runs the "main schedule"
284    pub fn run_main(world: &mut World, mut run_at_least_once: Local<bool>) {
285        if !*run_at_least_once {
286            world.resource_scope(|world, order: Mut<MainScheduleOrder>| {
287                for &label in &order.startup_labels {
288                    let _ = world.try_run_schedule(label);
289                }
290            });
291            *run_at_least_once = true;
292        }
293
294        world.resource_scope(|world, order: Mut<MainScheduleOrder>| {
295            for &label in &order.labels {
296                let _ = world.try_run_schedule(label);
297            }
298        });
299    }
300}
301
302/// Initializes the [`Main`] schedule, sub schedules, and resources for a given [`App`].
303pub struct MainSchedulePlugin;
304
305impl Plugin for MainSchedulePlugin {
306    fn build(&self, app: &mut App) {
307        // simple "facilitator" schedules benefit from simpler single threaded scheduling
308        let mut main_schedule = Schedule::new(Main);
309        main_schedule.set_executor_kind(ExecutorKind::SingleThreaded);
310        let mut fixed_main_schedule = Schedule::new(FixedMain);
311        fixed_main_schedule.set_executor_kind(ExecutorKind::SingleThreaded);
312        let mut fixed_main_loop_schedule = Schedule::new(RunFixedMainLoop);
313        fixed_main_loop_schedule.set_executor_kind(ExecutorKind::SingleThreaded);
314
315        app.add_schedule(main_schedule)
316            .add_schedule(fixed_main_schedule)
317            .add_schedule(fixed_main_loop_schedule)
318            .init_resource::<MainScheduleOrder>()
319            .init_resource::<FixedMainScheduleOrder>()
320            .add_systems(Main, Main::run_main)
321            .add_systems(FixedMain, FixedMain::run_fixed_main)
322            .configure_sets(
323                RunFixedMainLoop,
324                (
325                    RunFixedMainLoopSystems::BeforeFixedMainLoop,
326                    RunFixedMainLoopSystems::FixedMainLoop,
327                    RunFixedMainLoopSystems::AfterFixedMainLoop,
328                )
329                    .chain(),
330            );
331
332        #[cfg(feature = "bevy_debug_stepping")]
333        {
334            use bevy_ecs::schedule::{IntoScheduleConfigs, Stepping};
335            app.add_systems(Main, Stepping::begin_frame.before(Main::run_main));
336        }
337    }
338}
339
340/// Defines the schedules to be run for the [`FixedMain`] schedule, including
341/// their order.
342#[derive(Resource, Debug)]
343pub struct FixedMainScheduleOrder {
344    /// The labels to run for the [`FixedMain`] schedule (in the order they will be run).
345    pub labels: Vec<InternedScheduleLabel>,
346}
347
348impl Default for FixedMainScheduleOrder {
349    fn default() -> Self {
350        Self {
351            labels: vec![
352                FixedFirst.intern(),
353                FixedPreUpdate.intern(),
354                FixedUpdate.intern(),
355                FixedPostUpdate.intern(),
356                FixedLast.intern(),
357            ],
358        }
359    }
360}
361
362impl FixedMainScheduleOrder {
363    /// Adds the given `schedule` after the `after` schedule
364    pub fn insert_after(&mut self, after: impl ScheduleLabel, schedule: impl ScheduleLabel) {
365        let index = self
366            .labels
367            .iter()
368            .position(|current| (**current).eq(&after))
369            .unwrap_or_else(|| panic!("Expected {after:?} to exist"));
370        self.labels.insert(index + 1, schedule.intern());
371    }
372
373    /// Adds the given `schedule` before the `before` schedule
374    pub fn insert_before(&mut self, before: impl ScheduleLabel, schedule: impl ScheduleLabel) {
375        let index = self
376            .labels
377            .iter()
378            .position(|current| (**current).eq(&before))
379            .unwrap_or_else(|| panic!("Expected {before:?} to exist"));
380        self.labels.insert(index, schedule.intern());
381    }
382}
383
384impl FixedMain {
385    /// A system that runs the fixed timestep's "main schedule"
386    pub fn run_fixed_main(world: &mut World) {
387        world.resource_scope(|world, order: Mut<FixedMainScheduleOrder>| {
388            for &label in &order.labels {
389                let _ = world.try_run_schedule(label);
390            }
391        });
392    }
393}
394
395/// Set enum for the systems that want to run inside [`RunFixedMainLoop`],
396/// but before or after the fixed update logic. Systems in this set
397/// will run exactly once per frame, regardless of the number of fixed updates.
398/// They will also run under a variable timestep.
399///
400/// This is useful for handling things that need to run every frame, but
401/// also need to be read by the fixed update logic. See the individual variants
402/// for examples of what kind of systems should be placed in each.
403///
404/// Note that in contrast to most other Bevy schedules, systems added directly to
405/// [`RunFixedMainLoop`] will *not* be parallelized between each other.
406#[derive(Debug, Hash, PartialEq, Eq, Copy, Clone, SystemSet)]
407pub enum RunFixedMainLoopSystems {
408    /// Runs before the fixed update logic.
409    ///
410    /// A good example of a system that fits here
411    /// is camera movement, which needs to be updated in a variable timestep,
412    /// as you want the camera to move with as much precision and updates as
413    /// the frame rate allows. A physics system that needs to read the camera
414    /// position and orientation, however, should run in the fixed update logic,
415    /// as it needs to be deterministic and run at a fixed rate for better stability.
416    /// Note that we are not placing the camera movement system in `Update`, as that
417    /// would mean that the physics system already ran at that point.
418    ///
419    /// # Example
420    /// ```
421    /// # use bevy_app::prelude::*;
422    /// # use bevy_ecs::prelude::*;
423    /// App::new()
424    ///   .add_systems(
425    ///     RunFixedMainLoop,
426    ///     update_camera_rotation.in_set(RunFixedMainLoopSystems::BeforeFixedMainLoop))
427    ///   .add_systems(FixedUpdate, update_physics);
428    ///
429    /// # fn update_camera_rotation() {}
430    /// # fn update_physics() {}
431    /// ```
432    BeforeFixedMainLoop,
433    /// Contains the fixed update logic.
434    /// Runs [`FixedMain`] zero or more times based on delta of
435    /// [`Time<Virtual>`] and [`Time::overstep`].
436    ///
437    /// Don't place systems here, use [`FixedUpdate`] and friends instead.
438    /// Use this system instead to order your systems to run specifically inbetween the fixed update logic and all
439    /// other systems that run in [`RunFixedMainLoopSystems::BeforeFixedMainLoop`] or [`RunFixedMainLoopSystems::AfterFixedMainLoop`].
440    ///
441    /// [`Time<Virtual>`]: https://docs.rs/bevy/latest/bevy/prelude/struct.Virtual.html
442    /// [`Time::overstep`]: https://docs.rs/bevy/latest/bevy/time/struct.Time.html#method.overstep
443    /// # Example
444    /// ```
445    /// # use bevy_app::prelude::*;
446    /// # use bevy_ecs::prelude::*;
447    /// App::new()
448    ///   .add_systems(FixedUpdate, update_physics)
449    ///   .add_systems(
450    ///     RunFixedMainLoop,
451    ///     (
452    ///       // This system will be called before all interpolation systems
453    ///       // that third-party plugins might add.
454    ///       prepare_for_interpolation
455    ///         .after(RunFixedMainLoopSystems::FixedMainLoop)
456    ///         .before(RunFixedMainLoopSystems::AfterFixedMainLoop),
457    ///     )
458    ///   );
459    ///
460    /// # fn prepare_for_interpolation() {}
461    /// # fn update_physics() {}
462    /// ```
463    FixedMainLoop,
464    /// Runs after the fixed update logic.
465    ///
466    /// A good example of a system that fits here
467    /// is a system that interpolates the transform of an entity between the last and current fixed update.
468    /// See the [fixed timestep example] for more details.
469    ///
470    /// [fixed timestep example]: https://github.com/bevyengine/bevy/blob/main/examples/movement/physics_in_fixed_timestep.rs
471    ///
472    /// # Example
473    /// ```
474    /// # use bevy_app::prelude::*;
475    /// # use bevy_ecs::prelude::*;
476    /// App::new()
477    ///   .add_systems(FixedUpdate, update_physics)
478    ///   .add_systems(
479    ///     RunFixedMainLoop,
480    ///     interpolate_transforms.in_set(RunFixedMainLoopSystems::AfterFixedMainLoop));
481    ///
482    /// # fn interpolate_transforms() {}
483    /// # fn update_physics() {}
484    /// ```
485    AfterFixedMainLoop,
486}
487
488/// Deprecated alias for [`RunFixedMainLoopSystems`].
489#[deprecated(since = "0.17.0", note = "Renamed to `RunFixedMainLoopSystems`.")]
490pub type RunFixedMainLoopSystem = RunFixedMainLoopSystems;