Support system.in_schedule() and system.on_startup() (#7790)

# Objective

Support the following syntax for adding systems:

```rust
App::new()
    .add_system(setup.on_startup())
    .add_systems((
        show_menu.in_schedule(OnEnter(GameState::Paused)),
        menu_ssytem.in_set(OnUpdate(GameState::Paused)),
        hide_menu.in_schedule(OnExit(GameState::Paused)),
    ))
```

## Solution

Add the traits `IntoSystemAppConfig{s}`, which provide the extension methods necessary for configuring which schedule a system belongs to. These extension methods return `IntoSystemAppConfig{s}`, which `App::add_system{s}` uses to choose which schedule to add systems to.

---

## Changelog

+ Added the extension methods `in_schedule(label)` and  `on_startup()` for configuring the schedule a system belongs to.

## Future Work

* Replace all uses of `add_startup_system` in the engine.
* Deprecate this method
This commit is contained in:
JoJoJet 2023-02-24 18:33:55 +00:00
parent 40e90b51b5
commit b8263b55fb
32 changed files with 444 additions and 189 deletions

View File

@ -1,4 +1,7 @@
use crate::{CoreSchedule, CoreSet, Plugin, PluginGroup, StartupSet};
use crate::{
CoreSchedule, CoreSet, IntoSystemAppConfig, IntoSystemAppConfigs, Plugin, PluginGroup,
StartupSet, SystemAppConfig,
};
pub use bevy_derive::AppLabel;
use bevy_ecs::{
prelude::*,
@ -378,10 +381,18 @@ impl App {
/// #
/// app.add_system(my_system);
/// ```
pub fn add_system<M>(&mut self, system: impl IntoSystemConfig<M>) -> &mut Self {
pub fn add_system<M>(&mut self, system: impl IntoSystemAppConfig<M>) -> &mut Self {
let mut schedules = self.world.resource_mut::<Schedules>();
if let Some(default_schedule) = schedules.get_mut(&*self.default_schedule_label) {
let SystemAppConfig { system, schedule } = system.into_app_config();
if let Some(schedule_label) = schedule {
if let Some(schedule) = schedules.get_mut(&*schedule_label) {
schedule.add_system(system);
} else {
panic!("Schedule {schedule_label:?} does not exist.")
}
} else if let Some(default_schedule) = schedules.get_mut(&*self.default_schedule_label) {
default_schedule.add_system(system);
} else {
let schedule_label = &self.default_schedule_label;
@ -406,48 +417,28 @@ impl App {
/// #
/// app.add_systems((system_a, system_b, system_c));
/// ```
pub fn add_systems<M>(&mut self, systems: impl IntoSystemConfigs<M>) -> &mut Self {
pub fn add_systems<M>(&mut self, systems: impl IntoSystemAppConfigs<M>) -> &mut Self {
let mut schedules = self.world.resource_mut::<Schedules>();
if let Some(default_schedule) = schedules.get_mut(&*self.default_schedule_label) {
default_schedule.add_systems(systems);
} else {
let schedule_label = &self.default_schedule_label;
panic!("Default schedule {schedule_label:?} does not exist.")
}
self
}
/// Adds a system to the provided [`Schedule`].
pub fn add_system_to_schedule<M>(
&mut self,
schedule_label: impl ScheduleLabel,
system: impl IntoSystemConfig<M>,
) -> &mut Self {
let mut schedules = self.world.resource_mut::<Schedules>();
if let Some(schedule) = schedules.get_mut(&schedule_label) {
schedule.add_system(system);
} else {
panic!("Provided schedule {schedule_label:?} does not exist.")
}
self
}
/// Adds a collection of system to the provided [`Schedule`].
pub fn add_systems_to_schedule<M>(
&mut self,
schedule_label: impl ScheduleLabel,
systems: impl IntoSystemConfigs<M>,
) -> &mut Self {
let mut schedules = self.world.resource_mut::<Schedules>();
if let Some(schedule) = schedules.get_mut(&schedule_label) {
schedule.add_systems(systems);
} else {
panic!("Provided schedule {schedule_label:?} does not exist.")
match systems.into_app_configs().0 {
crate::InnerConfigs::Blanket { systems, schedule } => {
let schedule = if let Some(label) = schedule {
schedules
.get_mut(&*label)
.unwrap_or_else(|| panic!("Schedule '{label:?}' does not exist."))
} else {
let label = &*self.default_schedule_label;
schedules
.get_mut(label)
.unwrap_or_else(|| panic!("Default schedule '{label:?}' does not exist."))
};
schedule.add_systems(systems);
}
crate::InnerConfigs::Granular(systems) => {
for system in systems {
self.add_system(system);
}
}
}
self
@ -472,7 +463,7 @@ impl App {
/// .add_startup_system(my_startup_system);
/// ```
pub fn add_startup_system<M>(&mut self, system: impl IntoSystemConfig<M>) -> &mut Self {
self.add_system_to_schedule(CoreSchedule::Startup, system)
self.add_system(system.in_schedule(CoreSchedule::Startup))
}
/// Adds a collection of systems to [`CoreSchedule::Startup`].
@ -497,7 +488,7 @@ impl App {
/// );
/// ```
pub fn add_startup_systems<M>(&mut self, systems: impl IntoSystemConfigs<M>) -> &mut Self {
self.add_systems_to_schedule(CoreSchedule::Startup, systems)
self.add_systems(systems.into_configs().in_schedule(CoreSchedule::Startup))
}
/// Configures a system set in the default schedule, adding the set if it does not exist.

View File

@ -0,0 +1,291 @@
use bevy_ecs::{
all_tuples,
schedule::{
BoxedScheduleLabel, Condition, IntoSystemConfig, IntoSystemSet, ScheduleLabel,
SystemConfig, SystemConfigs, SystemSet,
},
};
use crate::CoreSchedule;
/// A [`System`] with [`App`]-aware scheduling metadata.
///
/// [`System`]: bevy_ecs::prelude::System
/// [`App`]: crate::App
pub struct SystemAppConfig {
pub(crate) system: SystemConfig,
pub(crate) schedule: Option<BoxedScheduleLabel>,
}
/// Types that can be converted into a [`SystemAppConfig`].
///
/// This has been implemented for all `System<In = (), Out = ()>` trait objects
/// and all functions that convert into such.
pub trait IntoSystemAppConfig<Marker>: Sized {
/// Converts into a [`SystemAppConfig`].
fn into_app_config(self) -> SystemAppConfig;
/// Adds the system to the provided `schedule`.
///
/// If a schedule is not specified, it will be added to the [`App`]'s default schedule.
///
/// [`App`]: crate::App
///
/// # Panics
///
/// If the system has already been assigned to a schedule.
#[track_caller]
fn in_schedule(self, schedule: impl ScheduleLabel) -> SystemAppConfig {
let mut config = self.into_app_config();
if let Some(old_schedule) = &config.schedule {
panic!(
"Cannot add system to schedule '{schedule:?}': it is already in '{old_schedule:?}'."
);
}
config.schedule = Some(Box::new(schedule));
config
}
/// Adds the system to [`CoreSchedule::Startup`].
/// This is a shorthand for `self.in_schedule(CoreSchedule::Startup)`.
///
/// Systems in this schedule will run exactly once, at the start of the [`App`]'s lifecycle.
///
/// [`App`]: crate::App
///
/// # Examples
///
/// ```
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// #
/// fn my_startup_system(_commands: Commands) {
/// println!("My startup system");
/// }
///
/// App::new()
/// .add_system(my_startup_system.on_startup())
/// .run();
/// ```
///
/// # Panics
///
/// If the system has already been assigned to a schedule.
#[inline]
fn on_startup(self) -> SystemAppConfig {
self.in_schedule(CoreSchedule::Startup)
}
}
impl IntoSystemConfig<(), Self> for SystemAppConfig {
fn into_config(self) -> Self {
self
}
#[track_caller]
fn in_set(self, set: impl SystemSet) -> Self {
let Self { system, schedule } = self;
Self {
system: system.in_set(set),
schedule,
}
}
#[track_caller]
fn in_base_set(self, set: impl SystemSet) -> Self {
let Self { system, schedule } = self;
Self {
system: system.in_base_set(set),
schedule,
}
}
fn no_default_base_set(self) -> Self {
let Self { system, schedule } = self;
Self {
system: system.no_default_base_set(),
schedule,
}
}
fn before<M>(self, set: impl IntoSystemSet<M>) -> Self {
let Self { system, schedule } = self;
Self {
system: system.before(set),
schedule,
}
}
fn after<M>(self, set: impl IntoSystemSet<M>) -> Self {
let Self { system, schedule } = self;
Self {
system: system.after(set),
schedule,
}
}
fn run_if<P>(self, condition: impl Condition<P>) -> Self {
let Self { system, schedule } = self;
Self {
system: system.run_if(condition),
schedule,
}
}
fn ambiguous_with<M>(self, set: impl IntoSystemSet<M>) -> Self {
let Self { system, schedule } = self;
Self {
system: system.ambiguous_with(set),
schedule,
}
}
fn ambiguous_with_all(self) -> Self {
let Self { system, schedule } = self;
Self {
system: system.ambiguous_with_all(),
schedule,
}
}
}
impl IntoSystemAppConfig<()> for SystemAppConfig {
fn into_app_config(self) -> SystemAppConfig {
self
}
}
impl<Marker, T> IntoSystemAppConfig<Marker> for T
where
T: IntoSystemConfig<Marker>,
{
fn into_app_config(self) -> SystemAppConfig {
SystemAppConfig {
system: self.into_config(),
schedule: None,
}
}
}
/// A collection of [`SystemAppConfig`]s.
pub struct SystemAppConfigs(pub(crate) InnerConfigs);
pub(crate) enum InnerConfigs {
/// This came from an instance of `SystemConfigs`.
/// All systems are in the same schedule.
Blanket {
systems: SystemConfigs,
schedule: Option<BoxedScheduleLabel>,
},
/// This came from several separate instances of `SystemAppConfig`.
/// Each system gets its own schedule.
Granular(Vec<SystemAppConfig>),
}
/// Types that can convert into [`SystemAppConfigs`].
pub trait IntoSystemAppConfigs<Marker>: Sized {
/// Converts to [`SystemAppConfigs`].
fn into_app_configs(self) -> SystemAppConfigs;
/// Adds the systems to the provided `schedule`.
///
/// If a schedule is not specified, they will be added to the [`App`]'s default schedule.
///
/// [`App`]: crate::App
///
/// # Panics
///
/// If any of the systems have already been assigned to a schedule.
#[track_caller]
fn in_schedule(self, label: impl ScheduleLabel) -> SystemAppConfigs {
let mut configs = self.into_app_configs();
match &mut configs.0 {
InnerConfigs::Blanket { schedule, .. } => {
if schedule.is_some() {
panic!(
"Cannot add systems to the schedule '{label:?}: they are already in '{schedule:?}'"
);
}
*schedule = Some(Box::new(label));
}
InnerConfigs::Granular(configs) => {
for SystemAppConfig { schedule, .. } in configs {
if schedule.is_some() {
panic!(
"Cannot add system to the schedule '{label:?}': it is already in '{schedule:?}'."
);
}
*schedule = Some(label.dyn_clone());
}
}
}
configs
}
/// Adds the systems to [`CoreSchedule::Startup`].
/// This is a shorthand for `self.in_schedule(CoreSchedule::Startup)`.
///
/// # Examples
///
/// ```
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// #
/// # let mut app = App::new();
/// # fn startup_system_a() {}
/// # fn startup_system_b() {}
/// # fn startup_system_c() {}
/// #
/// app.add_systems(
/// (
/// startup_system_a,
/// startup_system_b,
/// startup_system_c,
/// )
/// .on_startup()
/// );
/// ```
///
/// # Panics
///
/// If any of the systems have already been assigned to a schedule.
#[track_caller]
fn on_startup(self) -> SystemAppConfigs {
self.in_schedule(CoreSchedule::Startup)
}
}
impl IntoSystemAppConfigs<()> for SystemAppConfigs {
fn into_app_configs(self) -> SystemAppConfigs {
self
}
}
impl IntoSystemAppConfigs<()> for SystemConfigs {
fn into_app_configs(self) -> SystemAppConfigs {
SystemAppConfigs(InnerConfigs::Blanket {
systems: self,
schedule: None,
})
}
}
macro_rules! impl_system_collection {
($(($param: ident, $sys: ident)),*) => {
impl<$($param, $sys),*> IntoSystemAppConfigs<($($param,)*)> for ($($sys,)*)
where
$($sys: IntoSystemAppConfig<$param>),*
{
#[allow(non_snake_case)]
fn into_app_configs(self) -> SystemAppConfigs {
let ($($sys,)*) = self;
SystemAppConfigs(InnerConfigs::Granular(vec![$($sys.into_app_config(),)*]))
}
}
}
}
all_tuples!(impl_system_collection, 0, 15, P, S);

View File

@ -3,6 +3,7 @@
#![warn(missing_docs)]
mod app;
mod config;
mod plugin;
mod plugin_group;
mod schedule_runner;
@ -12,6 +13,7 @@ mod ci_testing;
pub use app::*;
pub use bevy_derive::DynamicPlugin;
pub use config::*;
pub use plugin::*;
pub use plugin_group::*;
pub use schedule_runner::*;
@ -23,7 +25,9 @@ pub mod prelude {
pub use crate::AppTypeRegistry;
#[doc(hidden)]
pub use crate::{
app::App, CoreSchedule, CoreSet, DynamicPlugin, Plugin, PluginGroup, StartupSet,
app::App,
config::{IntoSystemAppConfig, IntoSystemAppConfigs},
CoreSchedule, CoreSet, DynamicPlugin, Plugin, PluginGroup, StartupSet,
};
}

View File

@ -19,7 +19,7 @@ pub mod graph {
pub use camera_2d::*;
pub use main_pass_2d_node::*;
use bevy_app::{App, Plugin};
use bevy_app::{App, IntoSystemAppConfig, Plugin};
use bevy_ecs::prelude::*;
use bevy_render::{
camera::Camera,
@ -51,7 +51,7 @@ impl Plugin for Core2dPlugin {
render_app
.init_resource::<DrawFunctions<Transparent2d>>()
.add_system_to_schedule(ExtractSchedule, extract_core_2d_camera_phases)
.add_system(extract_core_2d_camera_phases.in_schedule(ExtractSchedule))
.add_system(sort_phase_system::<Transparent2d>.in_set(RenderSet::PhaseSort))
.add_system(
batch_phase_system::<Transparent2d>

View File

@ -22,7 +22,7 @@ use std::cmp::Reverse;
pub use camera_3d::*;
pub use main_pass_3d_node::*;
use bevy_app::{App, Plugin};
use bevy_app::{App, IntoSystemAppConfig, Plugin};
use bevy_ecs::prelude::*;
use bevy_render::{
camera::{Camera, ExtractedCamera},
@ -67,7 +67,7 @@ impl Plugin for Core3dPlugin {
.init_resource::<DrawFunctions<Opaque3d>>()
.init_resource::<DrawFunctions<AlphaMask3d>>()
.init_resource::<DrawFunctions<Transparent3d>>()
.add_system_to_schedule(ExtractSchedule, extract_core_3d_camera_phases)
.add_system(extract_core_3d_camera_phases.in_schedule(ExtractSchedule))
.add_system(
prepare_core_3d_depth_textures
.in_set(RenderSet::Prepare)

View File

@ -80,7 +80,7 @@ fn ambiguous_with(graph_info: &mut GraphInfo, set: BoxedSystemSet) {
/// Types that can be converted into a [`SystemSetConfig`].
///
/// This has been implemented for all types that implement [`SystemSet`] and boxed trait objects.
pub trait IntoSystemSetConfig: sealed::IntoSystemSetConfig {
pub trait IntoSystemSetConfig {
/// Convert into a [`SystemSetConfig`].
#[doc(hidden)]
fn into_config(self) -> SystemSetConfig;
@ -109,10 +109,7 @@ pub trait IntoSystemSetConfig: sealed::IntoSystemSetConfig {
fn ambiguous_with_all(self) -> SystemSetConfig;
}
impl<S> IntoSystemSetConfig for S
where
S: SystemSet + sealed::IntoSystemSetConfig,
{
impl<S: SystemSet> IntoSystemSetConfig for S {
fn into_config(self) -> SystemSetConfig {
SystemSetConfig::new(Box::new(self))
}
@ -274,38 +271,38 @@ impl IntoSystemSetConfig for SystemSetConfig {
///
/// This has been implemented for boxed [`System<In=(), Out=()>`](crate::system::System)
/// trait objects and all functions that turn into such.
pub trait IntoSystemConfig<Marker>: sealed::IntoSystemConfig<Marker> {
pub trait IntoSystemConfig<Marker, Config = SystemConfig> {
/// Convert into a [`SystemConfig`].
#[doc(hidden)]
fn into_config(self) -> SystemConfig;
fn into_config(self) -> Config;
/// Add to `set` membership.
#[track_caller]
fn in_set(self, set: impl SystemSet) -> SystemConfig;
fn in_set(self, set: impl SystemSet) -> Config;
/// Add to the provided "base" `set`. For more information on base sets, see [`SystemSet::is_base`].
#[track_caller]
fn in_base_set(self, set: impl SystemSet) -> SystemConfig;
fn in_base_set(self, set: impl SystemSet) -> Config;
/// Don't add this system to the schedules's default set.
fn no_default_base_set(self) -> SystemConfig;
fn no_default_base_set(self) -> Config;
/// Run before all systems in `set`.
fn before<M>(self, set: impl IntoSystemSet<M>) -> SystemConfig;
fn before<M>(self, set: impl IntoSystemSet<M>) -> Config;
/// Run after all systems in `set`.
fn after<M>(self, set: impl IntoSystemSet<M>) -> SystemConfig;
fn after<M>(self, set: impl IntoSystemSet<M>) -> Config;
/// Run only if the [`Condition`] is `true`.
///
/// The `Condition` will be evaluated at most once (per schedule run),
/// when the system prepares to run.
fn run_if<M>(self, condition: impl Condition<M>) -> SystemConfig;
fn run_if<M>(self, condition: impl Condition<M>) -> Config;
/// Suppress warnings and errors that would result from this system having ambiguities
/// (conflicting access but indeterminate order) with systems in `set`.
fn ambiguous_with<M>(self, set: impl IntoSystemSet<M>) -> SystemConfig;
fn ambiguous_with<M>(self, set: impl IntoSystemSet<M>) -> Config;
/// Suppress warnings and errors that would result from this system having ambiguities
/// (conflicting access but indeterminate order) with any other system.
fn ambiguous_with_all(self) -> SystemConfig;
fn ambiguous_with_all(self) -> Config;
}
impl<Marker, F> IntoSystemConfig<Marker> for F
where
F: IntoSystem<(), (), Marker> + sealed::IntoSystemConfig<Marker>,
F: IntoSystem<(), (), Marker>,
{
fn into_config(self) -> SystemConfig {
SystemConfig::new(Box::new(IntoSystem::into_system(self)))
@ -456,32 +453,6 @@ impl IntoSystemConfig<()> for SystemConfig {
}
}
// only `System<In=(), Out=()>` system objects can be scheduled
mod sealed {
use crate::{
schedule::{BoxedSystemSet, SystemSet},
system::{BoxedSystem, IntoSystem},
};
use super::{SystemConfig, SystemSetConfig};
pub trait IntoSystemConfig<Marker> {}
impl<Marker, F: IntoSystem<(), (), Marker>> IntoSystemConfig<Marker> for F {}
impl IntoSystemConfig<()> for BoxedSystem<(), ()> {}
impl IntoSystemConfig<()> for SystemConfig {}
pub trait IntoSystemSetConfig {}
impl<S: SystemSet> IntoSystemSetConfig for S {}
impl IntoSystemSetConfig for BoxedSystemSet {}
impl IntoSystemSetConfig for SystemSetConfig {}
}
/// A collection of [`SystemConfig`].
pub struct SystemConfigs {
pub(super) systems: Vec<SystemConfig>,

View File

@ -268,12 +268,12 @@ impl Plugin for PbrPlugin {
.configure_set(RenderLightSystems::PrepareLights.in_set(RenderSet::Prepare))
.configure_set(RenderLightSystems::PrepareClusters.in_set(RenderSet::Prepare))
.configure_set(RenderLightSystems::QueueShadows.in_set(RenderSet::Queue))
.add_systems_to_schedule(
ExtractSchedule,
.add_systems(
(
render::extract_clusters.in_set(RenderLightSystems::ExtractClusters),
render::extract_lights.in_set(RenderLightSystems::ExtractLights),
),
)
.in_schedule(ExtractSchedule),
)
.add_system(
render::prepare_lights

View File

@ -2,7 +2,7 @@ use crate::{
AlphaMode, DrawMesh, EnvironmentMapLight, MeshPipeline, MeshPipelineKey, MeshUniform,
PrepassPlugin, SetMeshBindGroup, SetMeshViewBindGroup,
};
use bevy_app::{App, Plugin};
use bevy_app::{App, IntoSystemAppConfig, Plugin};
use bevy_asset::{AddAsset, AssetEvent, AssetServer, Assets, Handle};
use bevy_core_pipeline::{
core_3d::{AlphaMask3d, Opaque3d, Transparent3d},
@ -195,7 +195,7 @@ where
.init_resource::<ExtractedMaterials<M>>()
.init_resource::<RenderMaterials<M>>()
.init_resource::<SpecializedMeshPipelines<MaterialPipeline<M>>>()
.add_system_to_schedule(ExtractSchedule, extract_materials::<M>)
.add_system(extract_materials::<M>.in_schedule(ExtractSchedule))
.add_system(
prepare_materials::<M>
.in_set(RenderSet::Prepare)

View File

@ -1,4 +1,4 @@
use bevy_app::Plugin;
use bevy_app::{IntoSystemAppConfig, Plugin};
use bevy_asset::{load_internal_asset, AssetServer, Handle, HandleUntyped};
use bevy_core_pipeline::{
prelude::Camera3d,
@ -97,7 +97,7 @@ where
};
render_app
.add_system_to_schedule(ExtractSchedule, extract_camera_prepass_phase)
.add_system(extract_camera_prepass_phase.in_schedule(ExtractSchedule))
.add_system(
prepare_prepass_textures
.in_set(RenderSet::Prepare)

View File

@ -5,7 +5,7 @@ use crate::{
ViewShadowBindings, CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, MAX_CASCADES_PER_LIGHT,
MAX_DIRECTIONAL_LIGHTS,
};
use bevy_app::Plugin;
use bevy_app::{IntoSystemAppConfigs, Plugin};
use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped};
use bevy_core_pipeline::{
prepass::ViewPrepassTextures,
@ -108,7 +108,7 @@ impl Plugin for MeshRenderPlugin {
render_app
.init_resource::<MeshPipeline>()
.init_resource::<SkinnedMeshUniform>()
.add_systems_to_schedule(ExtractSchedule, (extract_meshes, extract_skinned_meshes))
.add_systems((extract_meshes, extract_skinned_meshes).in_schedule(ExtractSchedule))
.add_system(prepare_skinned_meshes.in_set(RenderSet::Prepare))
.add_system(queue_mesh_bind_group.in_set(RenderSet::Queue))
.add_system(

View File

@ -8,7 +8,7 @@ pub use camera_driver_node::*;
pub use projection::*;
use crate::{render_graph::RenderGraph, ExtractSchedule, RenderApp};
use bevy_app::{App, Plugin};
use bevy_app::{App, IntoSystemAppConfig, Plugin};
#[derive(Default)]
pub struct CameraPlugin;
@ -26,7 +26,7 @@ impl Plugin for CameraPlugin {
.add_plugin(CameraProjectionPlugin::<PerspectiveProjection>::default());
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.add_system_to_schedule(ExtractSchedule, extract_cameras);
render_app.add_system(extract_cameras.in_schedule(ExtractSchedule));
let camera_driver_node = CameraDriverNode::new(&mut render_app.world);
let mut render_graph = render_app.world.resource_mut::<RenderGraph>();
render_graph.add_node(crate::main_graph::node::CAMERA_DRIVER, camera_driver_node);

View File

@ -4,7 +4,7 @@ use crate::{
view::ComputedVisibility,
Extract, ExtractSchedule, RenderApp, RenderSet,
};
use bevy_app::{App, Plugin};
use bevy_app::{App, IntoSystemAppConfig, Plugin};
use bevy_asset::{Asset, Handle};
use bevy_ecs::{
component::Component,
@ -180,9 +180,9 @@ impl<C: ExtractComponent> Plugin for ExtractComponentPlugin<C> {
fn build(&self, app: &mut App) {
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if self.only_extract_visible {
render_app.add_system_to_schedule(ExtractSchedule, extract_visible_components::<C>);
render_app.add_system(extract_visible_components::<C>.in_schedule(ExtractSchedule));
} else {
render_app.add_system_to_schedule(ExtractSchedule, extract_components::<C>);
render_app.add_system(extract_components::<C>.in_schedule(ExtractSchedule));
}
}
}

View File

@ -1,6 +1,6 @@
use std::marker::PhantomData;
use bevy_app::{App, Plugin};
use bevy_app::{App, IntoSystemAppConfig, Plugin};
use bevy_ecs::prelude::*;
pub use bevy_render_macros::ExtractResource;
@ -32,7 +32,7 @@ impl<R: ExtractResource> Default for ExtractResourcePlugin<R> {
impl<R: ExtractResource> Plugin for ExtractResourcePlugin<R> {
fn build(&self, app: &mut App) {
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.add_system_to_schedule(ExtractSchedule, extract_resource::<R>);
render_app.add_system(extract_resource::<R>.in_schedule(ExtractSchedule));
}
}
}

View File

@ -5,7 +5,7 @@ use crate::{
renderer::{RenderDevice, RenderQueue},
Extract, ExtractSchedule, RenderApp, RenderSet,
};
use bevy_app::{App, Plugin};
use bevy_app::{App, IntoSystemAppConfigs, Plugin};
use bevy_asset::{load_internal_asset, HandleUntyped};
use bevy_core::FrameCount;
use bevy_ecs::prelude::*;
@ -26,7 +26,7 @@ impl Plugin for GlobalsPlugin {
render_app
.init_resource::<GlobalsBuffer>()
.init_resource::<Time>()
.add_systems_to_schedule(ExtractSchedule, (extract_frame_count, extract_time))
.add_systems((extract_frame_count, extract_time).in_schedule(ExtractSchedule))
.add_system(prepare_globals_buffer.in_set(RenderSet::Prepare));
}
}

View File

@ -1,5 +1,5 @@
use crate::{Extract, ExtractSchedule, RenderApp, RenderSet};
use bevy_app::{App, Plugin};
use bevy_app::{App, IntoSystemAppConfig, Plugin};
use bevy_asset::{Asset, AssetEvent, Assets, Handle};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
@ -92,7 +92,7 @@ impl<A: RenderAsset> Plugin for RenderAssetPlugin<A> {
.init_resource::<ExtractedAssets<A>>()
.init_resource::<RenderAssets<A>>()
.init_resource::<PrepareNextFrameAssets<A>>()
.add_system_to_schedule(ExtractSchedule, extract_render_asset::<A>)
.add_system(extract_render_asset::<A>.in_schedule(ExtractSchedule))
.add_system(prepare_assets::<A>.in_set(self.prepare_asset_set.clone()));
}
}

View File

@ -3,7 +3,7 @@ use crate::{
renderer::{RenderAdapter, RenderDevice, RenderInstance},
Extract, ExtractSchedule, RenderApp, RenderSet,
};
use bevy_app::{App, Plugin};
use bevy_app::{App, IntoSystemAppConfig, Plugin};
use bevy_ecs::prelude::*;
use bevy_utils::{tracing::debug, HashMap, HashSet};
use bevy_window::{
@ -32,7 +32,7 @@ impl Plugin for WindowRenderPlugin {
.init_resource::<ExtractedWindows>()
.init_resource::<WindowSurfaces>()
.init_non_send_resource::<NonSendMarker>()
.add_system_to_schedule(ExtractSchedule, extract_windows)
.add_system(extract_windows.in_schedule(ExtractSchedule))
.configure_set(WindowSystem::Prepare.in_set(RenderSet::Prepare))
.add_system(prepare_windows.in_set(WindowSystem::Prepare));
}

View File

@ -70,12 +70,12 @@ impl Plugin for SpritePlugin {
.init_resource::<ExtractedSprites>()
.init_resource::<SpriteAssetEvents>()
.add_render_command::<Transparent2d, DrawSprite>()
.add_systems_to_schedule(
ExtractSchedule,
.add_systems(
(
extract_sprites.in_set(SpriteSystem::ExtractSprites),
extract_sprite_events,
),
)
.in_schedule(ExtractSchedule),
)
.add_system(
queue_sprites

View File

@ -1,4 +1,4 @@
use bevy_app::{App, Plugin};
use bevy_app::{App, IntoSystemAppConfig, Plugin};
use bevy_asset::{AddAsset, AssetEvent, AssetServer, Assets, Handle};
use bevy_core_pipeline::{
core_2d::Transparent2d,
@ -161,7 +161,7 @@ where
.init_resource::<ExtractedMaterials2d<M>>()
.init_resource::<RenderMaterials2d<M>>()
.init_resource::<SpecializedMeshPipelines<Material2dPipeline<M>>>()
.add_system_to_schedule(ExtractSchedule, extract_materials_2d::<M>)
.add_system(extract_materials_2d::<M>.in_schedule(ExtractSchedule))
.add_system(
prepare_materials_2d::<M>
.in_set(RenderSet::Prepare)

View File

@ -1,4 +1,4 @@
use bevy_app::Plugin;
use bevy_app::{IntoSystemAppConfig, Plugin};
use bevy_asset::{load_internal_asset, Handle, HandleUntyped};
use bevy_ecs::{
@ -103,7 +103,7 @@ impl Plugin for Mesh2dRenderPlugin {
render_app
.init_resource::<Mesh2dPipeline>()
.init_resource::<SpecializedMeshPipelines<Mesh2dPipeline>>()
.add_system_to_schedule(ExtractSchedule, extract_mesh2d)
.add_system(extract_mesh2d.in_schedule(ExtractSchedule))
.add_system(queue_mesh2d_bind_group.in_set(RenderSet::Queue))
.add_system(queue_mesh2d_view_bind_groups.in_set(RenderSet::Queue));
}

View File

@ -90,9 +90,10 @@ impl Plugin for TextPlugin {
);
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.add_system_to_schedule(
ExtractSchedule,
extract_text2d_sprite.after(SpriteSystem::ExtractSprites),
render_app.add_system(
extract_text2d_sprite
.after(SpriteSystem::ExtractSprites)
.in_schedule(ExtractSchedule),
);
}
}

View File

@ -76,15 +76,15 @@ pub fn build_ui_render(app: &mut App) {
.init_resource::<ExtractedUiNodes>()
.init_resource::<DrawFunctions<TransparentUi>>()
.add_render_command::<TransparentUi, DrawUi>()
.add_systems_to_schedule(
ExtractSchedule,
.add_systems(
(
extract_default_ui_camera_view::<Camera2d>,
extract_default_ui_camera_view::<Camera3d>,
extract_uinodes.in_set(RenderUiSystem::ExtractNode),
#[cfg(feature = "bevy_text")]
extract_text_uinodes.after(RenderUiSystem::ExtractNode),
),
)
.in_schedule(ExtractSchedule),
)
.add_system(prepare_uinodes.in_set(RenderSet::Prepare))
.add_system(queue_uinodes.in_set(RenderSet::Queue))

View File

@ -283,7 +283,7 @@ impl Plugin for ColoredMesh2dPlugin {
.add_render_command::<Transparent2d, DrawColoredMesh2d>()
.init_resource::<ColoredMesh2dPipeline>()
.init_resource::<SpecializedRenderPipelines<ColoredMesh2dPipeline>>()
.add_system_to_schedule(ExtractSchedule, extract_colored_mesh2d)
.add_system(extract_colored_mesh2d.in_schedule(ExtractSchedule))
.add_system(queue_colored_mesh2d.in_set(RenderSet::Queue));
}
}

View File

@ -9,13 +9,13 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_systems_to_schedule(
CoreSchedule::FixedUpdate,
.add_systems(
(
player_movement_system,
snap_to_player_system,
rotate_to_player_system,
),
)
.in_schedule(CoreSchedule::FixedUpdate),
)
.insert_resource(FixedTime::new_from_secs(TIME_STEP))
.add_system(bevy::window::close_on_esc)

View File

@ -8,9 +8,9 @@ fn main() {
.init_resource::<RpgSpriteHandles>()
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) // prevents blurry sprites
.add_state::<AppState>()
.add_system_to_schedule(OnEnter(AppState::Setup), load_textures)
.add_system(load_textures.in_schedule(OnEnter(AppState::Setup)))
.add_system(check_textures.in_set(OnUpdate(AppState::Setup)))
.add_system_to_schedule(OnEnter(AppState::Finished), setup)
.add_system(setup.in_schedule(OnEnter(AppState::Finished)))
.run();
}

View File

@ -9,7 +9,7 @@ fn main() {
// this system will run once every update (it should match your screen's refresh rate)
.add_system(frame_update)
// add our system to the fixed timestep schedule
.add_system_to_schedule(CoreSchedule::FixedUpdate, fixed_update)
.add_system(fixed_update.in_schedule(CoreSchedule::FixedUpdate))
// configure our fixed timestep schedule to run twice a second
.insert_resource(FixedTime::new_from_secs(FIXED_TIMESTEP))
.run();

View File

@ -42,16 +42,15 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_state::<AppState>()
.add_startup_system(setup_system)
.add_system(setup_system.on_startup())
.add_system(print_text_system)
.add_system(transition_to_in_game_system.in_set(OnUpdate(AppState::MainMenu)))
// add the cleanup systems
.add_system_to_schedule(
OnExit(AppState::MainMenu),
.add_systems((
// Pass in the types your system should operate on using the ::<T> (turbofish) syntax
cleanup_system::<MenuClose>,
)
.add_system_to_schedule(OnExit(AppState::InGame), cleanup_system::<LevelUnload>)
cleanup_system::<MenuClose>.in_schedule(OnExit(AppState::MainMenu)),
cleanup_system::<LevelUnload>.in_schedule(OnExit(AppState::InGame)),
))
.run();
}

View File

@ -14,7 +14,7 @@ fn main() {
})
.add_startup_system(generate_bodies)
.insert_resource(FixedTime::new_from_secs(DELTA_TIME))
.add_systems_to_schedule(CoreSchedule::FixedUpdate, (interact_bodies, integrate))
.add_systems((interact_bodies, integrate).in_schedule(CoreSchedule::FixedUpdate))
.add_system(look_at_star)
.insert_resource(ClearColor(Color::BLACK))
.run();

View File

@ -11,16 +11,16 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_state::<AppState>()
.add_startup_system(setup)
.add_system(setup.on_startup())
// This system runs when we enter `AppState::Menu`, during `CoreSet::StateTransitions`.
// All systems from the exit schedule of the state we're leaving are run first,
// and then all systems from the enter schedule of the state we're entering are run second.
.add_system_to_schedule(OnEnter(AppState::Menu), setup_menu)
.add_system(setup_menu.in_schedule(OnEnter(AppState::Menu)))
// By contrast, on_update systems are stored in the main schedule, during CoreSet::Update,
// and simply check the value of the `State<T>` resource to see if they should run each frame.
.add_system(menu.in_set(OnUpdate(AppState::Menu)))
.add_system_to_schedule(OnExit(AppState::Menu), cleanup_menu)
.add_system_to_schedule(OnEnter(AppState::InGame), setup_game)
.add_system(cleanup_menu.in_schedule(OnExit(AppState::Menu)))
.add_system(setup_game.in_schedule(OnEnter(AppState::InGame)))
.add_systems((movement, change_color).in_set(OnUpdate(AppState::InGame)))
.run();
}

View File

@ -24,8 +24,10 @@ fn main() {
)))
.add_plugins(DefaultPlugins)
.add_state::<GameState>()
.add_startup_system(setup_cameras)
.add_system_to_schedule(OnEnter(GameState::Playing), setup)
.add_systems((
setup_cameras.on_startup(),
setup.in_schedule(OnEnter(GameState::Playing)),
))
.add_systems(
(
move_player,
@ -36,10 +38,12 @@ fn main() {
)
.in_set(OnUpdate(GameState::Playing)),
)
.add_system_to_schedule(OnExit(GameState::Playing), teardown)
.add_system_to_schedule(OnEnter(GameState::GameOver), display_score)
.add_system(gameover_keyboard.in_set(OnUpdate(GameState::GameOver)))
.add_system_to_schedule(OnExit(GameState::GameOver), teardown)
.add_systems((
teardown.in_schedule(OnExit(GameState::Playing)),
display_score.in_schedule(OnEnter(GameState::GameOver)),
gameover_keyboard.in_set(OnUpdate(GameState::GameOver)),
teardown.in_schedule(OnExit(GameState::GameOver)),
))
.add_system(bevy::window::close_on_esc)
.run();
}

View File

@ -58,8 +58,7 @@ fn main() {
.add_startup_system(setup)
.add_event::<CollisionEvent>()
// Add our gameplay simulation systems to the fixed timestep schedule
.add_systems_to_schedule(
CoreSchedule::FixedUpdate,
.add_systems(
(
check_for_collisions,
apply_velocity.before(check_for_collisions),
@ -67,7 +66,8 @@ fn main() {
.before(check_for_collisions)
.after(apply_velocity),
play_collision_sound.after(check_for_collisions),
),
)
.in_schedule(CoreSchedule::FixedUpdate),
)
// Configure how frequently our gameplay systems are run
.insert_resource(FixedTime::new_from_secs(TIME_STEP))

View File

@ -60,13 +60,12 @@ mod splash {
// As this plugin is managing the splash screen, it will focus on the state `GameState::Splash`
app
// When entering the state, spawn everything needed for this screen
.add_system_to_schedule(OnEnter(GameState::Splash), splash_setup)
.add_system(splash_setup.in_schedule(OnEnter(GameState::Splash)))
// While in this state, run the `countdown` system
.add_system(countdown.in_set(OnUpdate(GameState::Splash)))
// When exiting the state, despawn everything that was spawned for this screen
.add_system_to_schedule(
OnExit(GameState::Splash),
despawn_screen::<OnSplashScreen>,
.add_system(
despawn_screen::<OnSplashScreen>.in_schedule(OnExit(GameState::Splash)),
);
}
}
@ -133,9 +132,11 @@ mod game {
impl Plugin for GamePlugin {
fn build(&self, app: &mut App) {
app.add_system_to_schedule(OnEnter(GameState::Game), game_setup)
.add_system(game.in_set(OnUpdate(GameState::Game)))
.add_system_to_schedule(OnExit(GameState::Game), despawn_screen::<OnGameScreen>);
app.add_systems((
game_setup.in_schedule(OnEnter(GameState::Game)),
game.in_set(OnUpdate(GameState::Game)),
despawn_screen::<OnGameScreen>.in_schedule(OnExit(GameState::Game)),
));
}
}
@ -268,38 +269,31 @@ mod menu {
// entering the `GameState::Menu` state.
// Current screen in the menu is handled by an independent state from `GameState`
.add_state::<MenuState>()
.add_system_to_schedule(OnEnter(GameState::Menu), menu_setup)
.add_system(menu_setup.in_schedule(OnEnter(GameState::Menu)))
// Systems to handle the main menu screen
.add_system_to_schedule(OnEnter(MenuState::Main), main_menu_setup)
.add_system_to_schedule(OnExit(MenuState::Main), despawn_screen::<OnMainMenuScreen>)
.add_systems((
main_menu_setup.in_schedule(OnEnter(MenuState::Main)),
despawn_screen::<OnMainMenuScreen>.in_schedule(OnExit(MenuState::Main)),
))
// Systems to handle the settings menu screen
.add_system_to_schedule(OnEnter(MenuState::Settings), settings_menu_setup)
.add_system_to_schedule(
OnExit(MenuState::Settings),
despawn_screen::<OnSettingsMenuScreen>,
)
.add_systems((
settings_menu_setup.in_schedule(OnEnter(MenuState::Settings)),
despawn_screen::<OnSettingsMenuScreen>.in_schedule(OnExit(MenuState::Settings)),
))
// Systems to handle the display settings screen
.add_system_to_schedule(
OnEnter(MenuState::SettingsDisplay),
display_settings_menu_setup,
)
.add_system(
.add_systems((
display_settings_menu_setup.in_schedule(OnEnter(MenuState::SettingsDisplay)),
setting_button::<DisplayQuality>.in_set(OnUpdate(MenuState::SettingsDisplay)),
)
.add_system_to_schedule(
OnExit(MenuState::SettingsDisplay),
despawn_screen::<OnDisplaySettingsMenuScreen>,
)
despawn_screen::<OnDisplaySettingsMenuScreen>
.in_schedule(OnExit(MenuState::SettingsDisplay)),
))
// Systems to handle the sound settings screen
.add_system_to_schedule(
OnEnter(MenuState::SettingsSound),
sound_settings_menu_setup,
)
.add_system(setting_button::<Volume>.in_set(OnUpdate(MenuState::SettingsSound)))
.add_system_to_schedule(
OnExit(MenuState::SettingsSound),
despawn_screen::<OnSoundSettingsMenuScreen>,
)
.add_systems((
sound_settings_menu_setup.in_schedule(OnEnter(MenuState::SettingsSound)),
setting_button::<Volume>.in_set(OnUpdate(MenuState::SettingsSound)),
despawn_screen::<OnSoundSettingsMenuScreen>
.in_schedule(OnExit(MenuState::SettingsSound)),
))
// Common systems to all screens that handles buttons behaviour
.add_systems((menu_action, button_system).in_set(OnUpdate(GameState::Menu)));
}

View File

@ -48,7 +48,7 @@ fn main() {
.add_system(movement_system)
.add_system(collision_system)
.add_system(counter_system)
.add_system_to_schedule(CoreSchedule::FixedUpdate, scheduled_spawner)
.add_system(scheduled_spawner.in_schedule(CoreSchedule::FixedUpdate))
.insert_resource(FixedTime::new_from_secs(0.2))
.run();
}