Skip to content

Commit 79b4f26

Browse files
authored
Add custom schedule example (#11527)
# Objective Fixes #11411 ## Solution - Added a simple example how to create and configure custom schedules that are run by the `Main` schedule. - Spot checked some of the API docs used, fixed `App::add_schedule` docs that referred to a function argument that was removed by #9600. ## Open Questions - While spot checking the docs, I noticed that the `Schedule` label is stored in a field called `name` instead of `label`. This seems unintuitive since the term label is used everywhere else. Should we change that field name? It was introduced in #9600. If so, I do think this change would be out of scope for this PR that mainly adds the example.
1 parent cd8dccb commit 79b4f26

File tree

4 files changed

+96
-2
lines changed

4 files changed

+96
-2
lines changed

Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1341,6 +1341,17 @@ description = "Change detection on components"
13411341
category = "ECS (Entity Component System)"
13421342
wasm = false
13431343

1344+
[[example]]
1345+
name = "custom_schedule"
1346+
path = "examples/ecs/custom_schedule.rs"
1347+
doc-scrape-examples = true
1348+
1349+
[package.metadata.example.custom_schedule]
1350+
name = "Custom Schedule"
1351+
description = "Demonstrates how to add custom schedules"
1352+
category = "ECS (Entity Component System)"
1353+
wasm = false
1354+
13441355
[[example]]
13451356
name = "custom_query_param"
13461357
path = "examples/ecs/custom_query_param.rs"

crates/bevy_app/src/app.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -853,10 +853,10 @@ impl App {
853853
.ok_or(label)
854854
}
855855

856-
/// Adds a new `schedule` to the [`App`] under the provided `label`.
856+
/// Adds a new `schedule` to the [`App`].
857857
///
858858
/// # Warning
859-
/// This method will overwrite any existing schedule at that label.
859+
/// This method will overwrite any existing schedule with the same name.
860860
/// To avoid this behavior, use the `init_schedule` method instead.
861861
pub fn add_schedule(&mut self, schedule: Schedule) -> &mut Self {
862862
let mut schedules = self.world.resource_mut::<Schedules>();

examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ Example | Description
225225
--- | ---
226226
[Component Change Detection](../examples/ecs/component_change_detection.rs) | Change detection on components
227227
[Custom Query Parameters](../examples/ecs/custom_query_param.rs) | Groups commonly used compound queries and query filters into a single type
228+
[Custom Schedule](../examples/ecs/custom_schedule.rs) | Demonstrates how to add custom schedules
228229
[Dynamic ECS](../examples/ecs/dynamic.rs) | Dynamically create components, spawn entities with those components and query those components
229230
[ECS Guide](../examples/ecs/ecs_guide.rs) | Full guide to Bevy's ECS
230231
[Event](../examples/ecs/event.rs) | Illustrates event creation, activation, and reception

examples/ecs/custom_schedule.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//! Demonstrates how to add custom schedules that run in Bevy's `Main` schedule, ordered relative to Bevy's built-in
2+
//! schedules such as `Update` or `Last`.
3+
4+
use bevy::app::MainScheduleOrder;
5+
use bevy::ecs::schedule::{ExecutorKind, ScheduleLabel};
6+
use bevy::prelude::*;
7+
8+
#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone)]
9+
struct SingleThreadedUpdate;
10+
11+
#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone)]
12+
struct CustomStartup;
13+
14+
fn main() {
15+
let mut app = App::new();
16+
17+
// Create a new [`Schedule`]. For demonstration purposes, we configure it to use a single threaded executor so that
18+
// systems in this schedule are never run in parallel. However, this is not a requirement for custom schedules in
19+
// general.
20+
let mut custom_update_schedule = Schedule::new(SingleThreadedUpdate);
21+
custom_update_schedule.set_executor_kind(ExecutorKind::SingleThreaded);
22+
23+
// Adding the schedule to the app does not automatically run the schedule. This merely registers the schedule so
24+
// that systems can look it up using the `Schedules` resource.
25+
app.add_schedule(custom_update_schedule);
26+
27+
// Bevy `App`s have a `main_schedule_label` field that configures which schedule is run by the App's `runner`.
28+
// By default, this is `Main`. The `Main` schedule is responsible for running Bevy's main schedules such as
29+
// `Update`, `Startup` or `Last`.
30+
//
31+
// We can configure the `Main` schedule to run our custom update schedule relative to the existing ones by modifying
32+
// the `MainScheduleOrder` resource.
33+
//
34+
// Note that we modify `MainScheduleOrder` directly in `main` and not in a startup system. The reason for this is
35+
// that the `MainScheduleOrder` cannot be modified from systems that are run as part of the `Main` schedule.
36+
let mut main_schedule_order = app.world.resource_mut::<MainScheduleOrder>();
37+
main_schedule_order.insert_after(Update, SingleThreadedUpdate);
38+
39+
// Adding a custom startup schedule works similarly, but needs to use `insert_startup_after`
40+
// instead of `insert_after`.
41+
app.add_schedule(Schedule::new(CustomStartup));
42+
43+
let mut main_schedule_order = app.world.resource_mut::<MainScheduleOrder>();
44+
main_schedule_order.insert_startup_after(PreStartup, CustomStartup);
45+
46+
app.add_systems(SingleThreadedUpdate, single_threaded_update_system)
47+
.add_systems(CustomStartup, custom_startup_system)
48+
.add_systems(PreStartup, pre_startup_system)
49+
.add_systems(Startup, startup_system)
50+
.add_systems(First, first_system)
51+
.add_systems(Update, update_system)
52+
.add_systems(Last, last_system)
53+
.run();
54+
}
55+
56+
fn pre_startup_system() {
57+
println!("Pre Startup");
58+
}
59+
60+
fn startup_system() {
61+
println!("Startup");
62+
}
63+
64+
fn custom_startup_system() {
65+
println!("Custom Startup");
66+
}
67+
68+
fn first_system() {
69+
println!("First");
70+
}
71+
72+
fn update_system() {
73+
println!("Update");
74+
}
75+
76+
fn single_threaded_update_system() {
77+
println!("Single Threaded Update");
78+
}
79+
80+
fn last_system() {
81+
println!("Last");
82+
}

0 commit comments

Comments
 (0)