Add documentation and examples of [ScheduleLabel] usage. (#19329)

## Objective

Add documentation useful to users of `bevy_ecs` not also using `App`.

Fixes #19270.

## Solution

* Add explanation of labels to `Schedule` documentation.
* Add example of `derive(ScheduleLabel)` to `trait ScheduleLabel`.
* Add a third example to `Schedule` which demonstrates using a schedule
via label instead of owning it directly.
* Add further explanation and links to `World::add_schedule()`, and
`World::run_schedule()`.

## Testing

Reviewed generated documentation.

Please review this documentation carefully for correctness, as I have
little experience with `bevy_ecs` and I am adding this information
because it would have helped my own past confusion, but I may still be
wrong about how things should be done.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: theotherphil <phil.j.ellison@gmail.com>
This commit is contained in:
Kevin Reid 2025-05-22 08:55:35 -07:00 committed by GitHub
parent c9e69ac65e
commit 870ad21238
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 79 additions and 3 deletions

View File

@ -257,8 +257,16 @@ impl Chain {
/// A collection of systems, and the metadata and executor needed to run them
/// in a certain order under certain conditions.
///
/// # Schedule labels
///
/// Each schedule has a [`ScheduleLabel`] value. This value is used to uniquely identify the
/// schedule when added to a [`World`]s [`Schedules`], and may be used to specify which schedule
/// a system should be added to.
///
/// # Example
///
/// Here is an example of a `Schedule` running a "Hello world" system:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// fn hello_world() { println!("Hello world!") }
@ -273,6 +281,7 @@ impl Chain {
/// ```
///
/// A schedule can also run several systems in an ordered way:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// fn system_one() { println!("System 1 works!") }
@ -291,6 +300,32 @@ impl Chain {
/// schedule.run(&mut world);
/// }
/// ```
///
/// Schedules are often inserted into a [`World`] and identified by their [`ScheduleLabel`] only:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// use bevy_ecs::schedule::ScheduleLabel;
///
/// // Declare a new schedule label.
/// #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
/// struct Update;
///
/// // This system shall be part of the schedule.
/// fn an_update_system() {
/// println!("Hello world!");
/// }
///
/// fn main() {
/// let mut world = World::new();
///
/// // Add a system to the schedule with that label (creating it automatically).
/// world.get_resource_or_init::<Schedules>().add_systems(Update, an_update_system);
///
/// // Run the schedule, and therefore run the system.
/// world.run_schedule(Update);
/// }
/// ```
pub struct Schedule {
label: InternedScheduleLabel,
graph: ScheduleGraph,
@ -327,7 +362,8 @@ impl Schedule {
this
}
/// Get the `InternedScheduleLabel` for this `Schedule`.
/// Returns the [`InternedScheduleLabel`] for this `Schedule`,
/// corresponding to the [`ScheduleLabel`] this schedule was created with.
pub fn label(&self) -> InternedScheduleLabel {
self.label
}

View File

@ -19,7 +19,39 @@ use crate::{
};
define_label!(
/// A strongly-typed class of labels used to identify a [`Schedule`](crate::schedule::Schedule).
/// A strongly-typed class of labels used to identify a [`Schedule`].
///
/// Each schedule in a [`World`] has a unique schedule label value, and
/// schedules can be automatically created from labels via [`Schedules::add_systems()`].
///
/// # Defining new schedule labels
///
/// By default, you should use Bevy's premade schedule labels which implement this trait.
/// If you are using [`bevy_ecs`] directly or if you need to run a group of systems outside
/// the existing schedules, you may define your own schedule labels by using
/// `#[derive(ScheduleLabel)]`.
///
/// ```
/// use bevy_ecs::prelude::*;
/// use bevy_ecs::schedule::ScheduleLabel;
///
/// // Declare a new schedule label.
/// #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
/// struct Update;
///
/// let mut world = World::new();
///
/// // Add a system to the schedule with that label (creating it automatically).
/// fn a_system_function() {}
/// world.get_resource_or_init::<Schedules>().add_systems(Update, a_system_function);
///
/// // Run the schedule, and therefore run the system.
/// world.run_schedule(Update);
/// ```
///
/// [`Schedule`]: crate::schedule::Schedule
/// [`Schedules::add_systems()`]: crate::schedule::Schedules::add_systems
/// [`World`]: crate::world::World
#[diagnostic::on_unimplemented(
note = "consider annotating `{Self}` with `#[derive(ScheduleLabel)]`"
)]

View File

@ -3402,11 +3402,18 @@ impl World {
// Schedule-related methods
impl World {
/// Adds the specified [`Schedule`] to the world. The schedule can later be run
/// Adds the specified [`Schedule`] to the world.
/// If a schedule already exists with the same [label](Schedule::label), it will be replaced.
///
/// The schedule can later be run
/// by calling [`.run_schedule(label)`](Self::run_schedule) or by directly
/// accessing the [`Schedules`] resource.
///
/// The `Schedules` resource will be initialized if it does not already exist.
///
/// An alternative to this is to call [`Schedules::add_systems()`] with some
/// [`ScheduleLabel`] and let the schedule for that label be created if it
/// does not already exist.
pub fn add_schedule(&mut self, schedule: Schedule) {
let mut schedules = self.get_resource_or_init::<Schedules>();
schedules.insert(schedule);
@ -3512,6 +3519,7 @@ impl World {
/// and system state is cached.
///
/// For simple testing use cases, call [`Schedule::run(&mut world)`](Schedule::run) instead.
/// This avoids the need to create a unique [`ScheduleLabel`].
///
/// # Panics
///