Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_app/src/main_schedule.rs
9401 views
1
use crate::{App, Plugin};
2
use alloc::{vec, vec::Vec};
3
use 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)]
57
pub 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)]
63
pub 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)]
69
pub 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)]
75
pub 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)]
81
pub 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)]
92
pub 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)]
104
pub 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)]
111
pub 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)]
118
pub 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)]
133
pub 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)]
141
pub 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)]
148
pub 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)]
160
pub 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)]
173
pub 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)]
179
pub 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)]
190
pub 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)]
196
pub struct Last;
197
198
/// Animation system set. This exists in [`PostUpdate`].
199
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
200
pub struct AnimationSystems;
201
202
/// Defines the schedules to be run for the [`Main`] schedule, including
203
/// their order.
204
#[derive(Resource, Debug)]
205
pub 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
212
impl 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
229
impl 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
279
impl 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`].
300
pub struct MainSchedulePlugin;
301
302
impl 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)]
340
pub 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
345
impl 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
359
impl 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
381
impl 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)]
404
pub 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
}
484
485