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