bevy_app/
main_schedule.rs

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