Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_app/src/main_schedule.rs
6595 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
/// * [`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)]
56
pub 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)]
62
pub 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)]
68
pub 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)]
74
pub 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)]
80
pub 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 an `Events` resource. This enables systems in [`Update`] to consume the events from the `Events`
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)]
91
pub 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)]
103
pub 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)]
110
pub 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)]
117
pub 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)]
132
pub 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)]
140
pub 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)]
147
pub 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)]
159
pub 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)]
172
pub 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)]
178
pub 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)]
189
pub 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)]
195
pub struct Last;
196
197
/// Animation system set. This exists in [`PostUpdate`].
198
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
199
pub struct AnimationSystems;
200
201
/// Deprecated alias for [`AnimationSystems`].
202
#[deprecated(since = "0.17.0", note = "Renamed to `AnimationSystems`.")]
203
pub type Animation = AnimationSystems;
204
205
/// Defines the schedules to be run for the [`Main`] schedule, including
206
/// their order.
207
#[derive(Resource, Debug)]
208
pub 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
215
impl 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
232
impl 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
282
impl 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`].
303
pub struct MainSchedulePlugin;
304
305
impl 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)]
343
pub 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
348
impl 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
362
impl 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
384
impl 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)]
407
pub 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`.")]
490
pub type RunFixedMainLoopSystem = RunFixedMainLoopSystems;
491
492