bevy_app/
main_schedule.rs

1use crate::{App, Plugin};
2use bevy_ecs::{
3    schedule::{ExecutorKind, InternedScheduleLabel, Schedule, ScheduleLabel},
4    system::{Local, Resource},
5    world::{Mut, World},
6};
7
8/// The schedule that contains the app logic that is evaluated each tick of [`App::update()`].
9///
10/// By default, it will run the following schedules in the given order:
11///
12/// On the first run of the schedule (and only on the first run), it will run:
13/// * [`PreStartup`]
14/// * [`Startup`]
15/// * [`PostStartup`]
16///
17/// Then it will run:
18/// * [`First`]
19/// * [`PreUpdate`]
20/// * [`StateTransition`](bevy_state::transition::StateTransition)
21/// * [`RunFixedMainLoop`]
22///     * This will run [`FixedMain`] zero to many times, based on how much time has elapsed.
23/// * [`Update`]
24/// * [`PostUpdate`]
25/// * [`Last`]
26///
27/// # Rendering
28///
29/// Note rendering is not executed in the main schedule by default.
30/// Instead, rendering is performed in a separate [`SubApp`]
31/// which exchanges data with the main app in between the main schedule runs.
32///
33/// See [`RenderPlugin`] and [`PipelinedRenderingPlugin`] for more details.
34///
35/// [`RenderPlugin`]: https://docs.rs/bevy/latest/bevy/render/struct.RenderPlugin.html
36/// [`PipelinedRenderingPlugin`]: https://docs.rs/bevy/latest/bevy/render/pipelined_rendering/struct.PipelinedRenderingPlugin.html
37/// [`SubApp`]: crate::SubApp
38#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
39pub struct Main;
40
41/// The schedule that runs before [`Startup`].
42///
43/// See the [`Main`] schedule for some details about how schedules are run.
44#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
45pub struct PreStartup;
46
47/// The schedule that runs once when the app starts.
48///
49/// See the [`Main`] schedule for some details about how schedules are run.
50#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
51pub struct Startup;
52
53/// The schedule that runs once after [`Startup`].
54///
55/// See the [`Main`] schedule for some details about how schedules are run.
56#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
57pub struct PostStartup;
58
59/// Runs first in the schedule.
60///
61/// See the [`Main`] schedule for some details about how schedules are run.
62#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
63pub struct First;
64
65/// The schedule that contains logic that must run before [`Update`]. For example, a system that reads raw keyboard
66/// input OS events into an `Events` resource. This enables systems in [`Update`] to consume the events from the `Events`
67/// resource without actually knowing about (or taking a direct scheduler dependency on) the "os-level keyboard event system".
68///
69/// [`PreUpdate`] exists to do "engine/plugin preparation work" that ensures the APIs consumed in [`Update`] are "ready".
70/// [`PreUpdate`] abstracts out "pre work implementation details".
71///
72/// See the [`Main`] schedule for some details about how schedules are run.
73#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
74pub struct PreUpdate;
75
76/// Runs the [`FixedMain`] schedule in a loop according until all relevant elapsed time has been "consumed".
77///
78/// See the [`Main`] schedule for some details about how schedules are run.
79#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
80pub struct RunFixedMainLoop;
81
82/// Runs first in the [`FixedMain`] schedule.
83///
84/// See the [`FixedMain`] schedule for details on how fixed updates work.
85/// See the [`Main`] schedule for some details about how schedules are run.
86#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
87pub struct FixedFirst;
88
89/// The schedule that contains logic that must run before [`FixedUpdate`].
90///
91/// See the [`FixedMain`] schedule for details on how fixed updates work.
92/// See the [`Main`] schedule for some details about how schedules are run.
93#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
94pub struct FixedPreUpdate;
95
96/// The schedule that contains most gameplay logic.
97///
98/// See the [`FixedMain`] schedule for details on how fixed updates work.
99/// See the [`Main`] schedule for some details about how schedules are run.
100#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
101pub struct FixedUpdate;
102
103/// The schedule that runs after the [`FixedUpdate`] schedule, for reacting
104/// to changes made in the main update logic.
105///
106/// See the [`FixedMain`] schedule for details on how fixed updates work.
107/// See the [`Main`] schedule for some details about how schedules are run.
108#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
109pub struct FixedPostUpdate;
110
111/// The schedule that runs last in [`FixedMain`]
112///
113/// See the [`FixedMain`] schedule for details on how fixed updates work.
114/// See the [`Main`] schedule for some details about how schedules are run.
115#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
116pub struct FixedLast;
117
118/// The schedule that contains systems which only run after a fixed period of time has elapsed.
119///
120/// The exclusive `run_fixed_main_schedule` system runs this schedule.
121/// This is run by the [`RunFixedMainLoop`] schedule.
122///
123/// Frequency of execution is configured by inserting `Time<Fixed>` resource, 64 Hz by default.
124/// See [this example](https://github.com/bevyengine/bevy/blob/latest/examples/time/time.rs).
125///
126/// See the [`Main`] schedule for some details about how schedules are run.
127#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
128pub struct FixedMain;
129
130/// The schedule that contains app logic. Ideally containing anything that must run once per
131/// render frame, such as UI.
132///
133/// See the [`Main`] schedule for some details about how schedules are run.
134#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
135pub struct Update;
136
137/// The schedule that contains scene spawning.
138///
139/// See the [`Main`] schedule for some details about how schedules are run.
140#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
141pub struct SpawnScene;
142
143/// The schedule that contains logic that must run after [`Update`]. For example, synchronizing "local transforms" in a hierarchy
144/// to "global" absolute transforms. This enables the [`PostUpdate`] transform-sync system to react to "local transform" changes in
145/// [`Update`] without the [`Update`] systems needing to know about (or add scheduler dependencies for) the "global transform sync system".
146///
147/// [`PostUpdate`] exists to do "engine/plugin response work" to things that happened in [`Update`].
148/// [`PostUpdate`] abstracts out "implementation details" from users defining systems in [`Update`].
149///
150/// See the [`Main`] schedule for some details about how schedules are run.
151#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
152pub struct PostUpdate;
153
154/// Runs last in the schedule.
155///
156/// See the [`Main`] schedule for some details about how schedules are run.
157#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
158pub struct Last;
159
160/// Defines the schedules to be run for the [`Main`] schedule, including
161/// their order.
162#[derive(Resource, Debug)]
163pub struct MainScheduleOrder {
164    /// The labels to run for the main phase of the [`Main`] schedule (in the order they will be run).
165    pub labels: Vec<InternedScheduleLabel>,
166    /// The labels to run for the startup phase of the [`Main`] schedule (in the order they will be run).
167    pub startup_labels: Vec<InternedScheduleLabel>,
168}
169
170impl Default for MainScheduleOrder {
171    fn default() -> Self {
172        Self {
173            labels: vec![
174                First.intern(),
175                PreUpdate.intern(),
176                RunFixedMainLoop.intern(),
177                Update.intern(),
178                SpawnScene.intern(),
179                PostUpdate.intern(),
180                Last.intern(),
181            ],
182            startup_labels: vec![PreStartup.intern(), Startup.intern(), PostStartup.intern()],
183        }
184    }
185}
186
187impl MainScheduleOrder {
188    /// Adds the given `schedule` after the `after` schedule in the main list of schedules.
189    pub fn insert_after(&mut self, after: impl ScheduleLabel, schedule: impl ScheduleLabel) {
190        let index = self
191            .labels
192            .iter()
193            .position(|current| (**current).eq(&after))
194            .unwrap_or_else(|| panic!("Expected {after:?} to exist"));
195        self.labels.insert(index + 1, schedule.intern());
196    }
197
198    /// Adds the given `schedule` before the `before` schedule in the main list of schedules.
199    pub fn insert_before(&mut self, before: impl ScheduleLabel, schedule: impl ScheduleLabel) {
200        let index = self
201            .labels
202            .iter()
203            .position(|current| (**current).eq(&before))
204            .unwrap_or_else(|| panic!("Expected {before:?} to exist"));
205        self.labels.insert(index, schedule.intern());
206    }
207
208    /// Adds the given `schedule` after the `after` schedule in the list of startup schedules.
209    pub fn insert_startup_after(
210        &mut self,
211        after: impl ScheduleLabel,
212        schedule: impl ScheduleLabel,
213    ) {
214        let index = self
215            .startup_labels
216            .iter()
217            .position(|current| (**current).eq(&after))
218            .unwrap_or_else(|| panic!("Expected {after:?} to exist"));
219        self.startup_labels.insert(index + 1, schedule.intern());
220    }
221
222    /// Adds the given `schedule` before the `before` schedule in the list of startup schedules.
223    pub fn insert_startup_before(
224        &mut self,
225        before: impl ScheduleLabel,
226        schedule: impl ScheduleLabel,
227    ) {
228        let index = self
229            .startup_labels
230            .iter()
231            .position(|current| (**current).eq(&before))
232            .unwrap_or_else(|| panic!("Expected {before:?} to exist"));
233        self.startup_labels.insert(index, schedule.intern());
234    }
235}
236
237impl Main {
238    /// A system that runs the "main schedule"
239    pub fn run_main(world: &mut World, mut run_at_least_once: Local<bool>) {
240        if !*run_at_least_once {
241            world.resource_scope(|world, order: Mut<MainScheduleOrder>| {
242                for &label in &order.startup_labels {
243                    let _ = world.try_run_schedule(label);
244                }
245            });
246            *run_at_least_once = true;
247        }
248
249        world.resource_scope(|world, order: Mut<MainScheduleOrder>| {
250            for &label in &order.labels {
251                let _ = world.try_run_schedule(label);
252            }
253        });
254    }
255}
256
257/// Initializes the [`Main`] schedule, sub schedules, and resources for a given [`App`].
258pub struct MainSchedulePlugin;
259
260impl Plugin for MainSchedulePlugin {
261    fn build(&self, app: &mut App) {
262        // simple "facilitator" schedules benefit from simpler single threaded scheduling
263        let mut main_schedule = Schedule::new(Main);
264        main_schedule.set_executor_kind(ExecutorKind::SingleThreaded);
265        let mut fixed_main_schedule = Schedule::new(FixedMain);
266        fixed_main_schedule.set_executor_kind(ExecutorKind::SingleThreaded);
267        let mut fixed_main_loop_schedule = Schedule::new(RunFixedMainLoop);
268        fixed_main_loop_schedule.set_executor_kind(ExecutorKind::SingleThreaded);
269
270        app.add_schedule(main_schedule)
271            .add_schedule(fixed_main_schedule)
272            .add_schedule(fixed_main_loop_schedule)
273            .init_resource::<MainScheduleOrder>()
274            .init_resource::<FixedMainScheduleOrder>()
275            .add_systems(Main, Main::run_main)
276            .add_systems(FixedMain, FixedMain::run_fixed_main);
277
278        #[cfg(feature = "bevy_debug_stepping")]
279        {
280            use bevy_ecs::schedule::{IntoSystemConfigs, Stepping};
281            app.add_systems(Main, Stepping::begin_frame.before(Main::run_main));
282        }
283    }
284}
285
286/// Defines the schedules to be run for the [`FixedMain`] schedule, including
287/// their order.
288#[derive(Resource, Debug)]
289pub struct FixedMainScheduleOrder {
290    /// The labels to run for the [`FixedMain`] schedule (in the order they will be run).
291    pub labels: Vec<InternedScheduleLabel>,
292}
293
294impl Default for FixedMainScheduleOrder {
295    fn default() -> Self {
296        Self {
297            labels: vec![
298                FixedFirst.intern(),
299                FixedPreUpdate.intern(),
300                FixedUpdate.intern(),
301                FixedPostUpdate.intern(),
302                FixedLast.intern(),
303            ],
304        }
305    }
306}
307
308impl FixedMainScheduleOrder {
309    /// Adds the given `schedule` after the `after` schedule
310    pub fn insert_after(&mut self, after: impl ScheduleLabel, schedule: impl ScheduleLabel) {
311        let index = self
312            .labels
313            .iter()
314            .position(|current| (**current).eq(&after))
315            .unwrap_or_else(|| panic!("Expected {after:?} to exist"));
316        self.labels.insert(index + 1, schedule.intern());
317    }
318
319    /// Adds the given `schedule` before the `before` schedule
320    pub fn insert_before(&mut self, before: impl ScheduleLabel, schedule: impl ScheduleLabel) {
321        let index = self
322            .labels
323            .iter()
324            .position(|current| (**current).eq(&before))
325            .unwrap_or_else(|| panic!("Expected {before:?} to exist"));
326        self.labels.insert(index, schedule.intern());
327    }
328}
329
330impl FixedMain {
331    /// A system that runs the fixed timestep's "main schedule"
332    pub fn run_fixed_main(world: &mut World) {
333        world.resource_scope(|world, order: Mut<FixedMainScheduleOrder>| {
334            for &label in &order.labels {
335                let _ = world.try_run_schedule(label);
336            }
337        });
338    }
339}