diff --git a/Cargo.toml b/Cargo.toml index 2bf46c8a7f777..3280002be3532 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -193,10 +193,18 @@ path = "examples/diagnostics/print_diagnostics.rs" name = "event" path = "examples/ecs/event.rs" +[[example]] +name = "fixed_timestep" +path = "examples/ecs/fixed_timestep.rs" + [[example]] name = "startup_system" path = "examples/ecs/startup_system.rs" +[[example]] +name = "state" +path = "examples/ecs/state.rs" + [[example]] name = "system_chaining" path = "examples/ecs/system_chaining.rs" diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 61d447fd286f2..adf2091ef2932 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -1,5 +1,5 @@ use crate::app_builder::AppBuilder; -use bevy_ecs::{ParallelExecutor, Resources, Schedule, World}; +use bevy_ecs::{Resources, Schedule, World}; #[cfg(feature = "trace")] use bevy_utils::tracing::info_span; @@ -30,9 +30,6 @@ pub struct App { pub resources: Resources, pub runner: Box, pub schedule: Schedule, - pub executor: ParallelExecutor, - pub startup_schedule: Schedule, - pub startup_executor: ParallelExecutor, } impl Default for App { @@ -41,16 +38,12 @@ impl Default for App { world: Default::default(), resources: Default::default(), schedule: Default::default(), - executor: Default::default(), - startup_schedule: Default::default(), - startup_executor: ParallelExecutor::without_tracker_clears(), runner: Box::new(run_once), } } } fn run_once(mut app: App) { - app.initialize(); app.update(); } @@ -61,34 +54,15 @@ impl App { pub fn update(&mut self) { self.schedule - .initialize(&mut self.world, &mut self.resources); - self.executor - .run(&mut self.schedule, &mut self.world, &mut self.resources); - } - - pub fn initialize(&mut self) { - #[cfg(feature = "trace")] - let startup_schedule_span = info_span!("startup_schedule"); - #[cfg(feature = "trace")] - let _startup_schedule_guard = startup_schedule_span.enter(); - self.startup_schedule - .initialize(&mut self.world, &mut self.resources); - self.startup_executor.initialize(&mut self.resources); - self.startup_executor.run( - &mut self.startup_schedule, - &mut self.world, - &mut self.resources, - ); + .initialize_and_run(&mut self.world, &mut self.resources); } pub fn run(mut self) { #[cfg(feature = "trace")] - let bevy_app_run_span = info_span!("bevy_app_run"); + let bevy_app_run_span = info_span!("bevy_app"); #[cfg(feature = "trace")] let _bevy_app_run_guard = bevy_app_run_span.enter(); - self.executor.initialize(&mut self.resources); - let runner = std::mem::replace(&mut self.runner, Box::new(run_once)); (runner)(self); } diff --git a/crates/bevy_app/src/app_builder.rs b/crates/bevy_app/src/app_builder.rs index 68e0063da74c7..a25198bcabdb1 100644 --- a/crates/bevy_app/src/app_builder.rs +++ b/crates/bevy_app/src/app_builder.rs @@ -1,10 +1,15 @@ +use std::any::Any; + use crate::{ app::{App, AppExit}, event::Events, plugin::Plugin, stage, startup_stage, PluginGroup, PluginGroupBuilder, }; -use bevy_ecs::{FromResources, IntoSystem, Resources, System, World}; +use bevy_ecs::{ + clear_trackers_system, FromResources, IntoStage, IntoSystem, Resource, Resources, RunOnce, + Schedule, Stage, State, StateStage, System, SystemStage, World, +}; use bevy_utils::tracing::debug; /// Configure [App]s using the builder pattern @@ -18,8 +23,10 @@ impl Default for AppBuilder { app: App::default(), }; - app_builder.add_default_stages(); - app_builder.add_event::(); + app_builder + .add_default_stages() + .add_event::() + .add_system_to_stage(stage::LAST, clear_trackers_system); app_builder } } @@ -49,55 +56,88 @@ impl AppBuilder { self } - pub fn add_stage(&mut self, stage_name: &'static str) -> &mut Self { - self.app.schedule.add_stage(stage_name); + pub fn add_stage>( + &mut self, + name: &'static str, + stage: S, + ) -> &mut Self { + self.app.schedule.add_stage(name, stage); self } - pub fn add_stage_after(&mut self, target: &'static str, stage_name: &'static str) -> &mut Self { - self.app.schedule.add_stage_after(target, stage_name); + pub fn add_stage_after>( + &mut self, + target: &'static str, + name: &'static str, + stage: S, + ) -> &mut Self { + self.app.schedule.add_stage_after(target, name, stage); self } - pub fn add_stage_before( + pub fn add_stage_before>( &mut self, target: &'static str, - stage_name: &'static str, + name: &'static str, + stage: S, ) -> &mut Self { - self.app.schedule.add_stage_before(target, stage_name); + self.app.schedule.add_stage_before(target, name, stage); self } - pub fn add_startup_stage(&mut self, stage_name: &'static str) -> &mut Self { - self.app.startup_schedule.add_stage(stage_name); + pub fn add_startup_stage>( + &mut self, + name: &'static str, + stage: S, + ) -> &mut Self { + self.app + .schedule + .stage(stage::STARTUP, |schedule: &mut Schedule| { + schedule.add_stage(name, stage) + }); self } - pub fn add_startup_stage_after( + pub fn add_startup_stage_after>( &mut self, target: &'static str, - stage_name: &'static str, + name: &'static str, + stage: S, ) -> &mut Self { self.app - .startup_schedule - .add_stage_after(target, stage_name); + .schedule + .stage(stage::STARTUP, |schedule: &mut Schedule| { + schedule.add_stage_after(target, name, stage) + }); self } - pub fn add_startup_stage_before( + pub fn add_startup_stage_before>( &mut self, target: &'static str, - stage_name: &'static str, + name: &'static str, + stage: S, ) -> &mut Self { self.app - .startup_schedule - .add_stage_before(target, stage_name); + .schedule + .stage(stage::STARTUP, |schedule: &mut Schedule| { + schedule.add_stage_before(target, name, stage) + }); + self + } + + pub fn stage &mut T>( + &mut self, + name: &str, + func: F, + ) -> &mut Self { + self.app.schedule.stage(name, func); self } pub fn add_system(&mut self, system: IntoS) -> &mut Self where - S: System, + S: System, IntoS: IntoSystem, { self.add_system_to_stage(stage::UPDATE, system) @@ -109,37 +149,41 @@ impl AppBuilder { system: IntoS, ) -> &mut Self where - S: System, + S: System, IntoS: IntoSystem, { self.app - .startup_schedule - .add_system_to_stage(stage_name, system); + .schedule + .stage(stage::STARTUP, |schedule: &mut Schedule| { + schedule.add_system_to_stage(stage_name, system) + }); self } pub fn add_startup_system(&mut self, system: IntoS) -> &mut Self where - S: System, + S: System, IntoS: IntoSystem, { - self.app - .startup_schedule - .add_system_to_stage(startup_stage::STARTUP, system); - self + self.add_startup_system_to_stage(startup_stage::STARTUP, system) } pub fn add_default_stages(&mut self) -> &mut Self { - self.add_startup_stage(startup_stage::PRE_STARTUP) - .add_startup_stage(startup_stage::STARTUP) - .add_startup_stage(startup_stage::POST_STARTUP) - .add_stage(stage::FIRST) - .add_stage(stage::PRE_EVENT) - .add_stage(stage::EVENT) - .add_stage(stage::PRE_UPDATE) - .add_stage(stage::UPDATE) - .add_stage(stage::POST_UPDATE) - .add_stage(stage::LAST) + self.add_stage( + stage::STARTUP, + Schedule::default() + .with_run_criteria(RunOnce::default()) + .with_stage(startup_stage::PRE_STARTUP, SystemStage::parallel()) + .with_stage(startup_stage::STARTUP, SystemStage::parallel()) + .with_stage(startup_stage::POST_STARTUP, SystemStage::parallel()), + ) + .add_stage(stage::FIRST, SystemStage::parallel()) + .add_stage(stage::PRE_EVENT, SystemStage::parallel()) + .add_stage(stage::EVENT, SystemStage::parallel()) + .add_stage(stage::PRE_UPDATE, SystemStage::parallel()) + .add_stage(stage::UPDATE, SystemStage::parallel()) + .add_stage(stage::POST_UPDATE, SystemStage::parallel()) + .add_stage(stage::LAST, SystemStage::parallel()) } pub fn add_system_to_stage( @@ -148,28 +192,13 @@ impl AppBuilder { system: IntoS, ) -> &mut Self where - S: System, + S: System, IntoS: IntoSystem, { self.app.schedule.add_system_to_stage(stage_name, system); self } - pub fn add_system_to_stage_front( - &mut self, - stage_name: &'static str, - system: IntoS, - ) -> &mut Self - where - S: System, - IntoS: IntoSystem, - { - self.app - .schedule - .add_system_to_stage_front(stage_name, system.system()); - self - } - pub fn add_event(&mut self) -> &mut Self where T: Send + Sync + 'static, @@ -178,6 +207,53 @@ impl AppBuilder { .add_system_to_stage(stage::EVENT, Events::::update_system) } + pub fn state_stage_name() -> String { + format!("state({})", std::any::type_name::()) + } + + pub fn add_state(&mut self, initial: T) -> &mut Self { + self.add_resource(State::new(initial)); + self.app.schedule.add_stage_after( + stage::UPDATE, + &Self::state_stage_name::(), + StateStage::::default(), + ); + self + } + + pub fn on_state_enter>( + &mut self, + value: T, + stage: S, + ) -> &mut Self { + self.stage( + &Self::state_stage_name::(), + |state_stage: &mut StateStage| state_stage.on_state_enter(value, stage), + ) + } + + pub fn on_state_update>( + &mut self, + value: T, + stage: S, + ) -> &mut Self { + self.stage( + &Self::state_stage_name::(), + |state_stage: &mut StateStage| state_stage.on_state_update(value, stage), + ) + } + + pub fn on_state_exit>( + &mut self, + value: T, + stage: S, + ) -> &mut Self { + self.stage( + &Self::state_stage_name::(), + |state_stage: &mut StateStage| state_stage.on_state_exit(value, stage), + ) + } + /// Adds a resource to the current [App] and overwrites any resource previously added of the same type. pub fn add_resource(&mut self, resource: T) -> &mut Self where diff --git a/crates/bevy_app/src/schedule_runner.rs b/crates/bevy_app/src/schedule_runner.rs index 9074685d898a4..c533720406001 100644 --- a/crates/bevy_app/src/schedule_runner.rs +++ b/crates/bevy_app/src/schedule_runner.rs @@ -56,8 +56,6 @@ impl Plugin for ScheduleRunnerPlugin { .get_or_insert_with(ScheduleRunnerSettings::default) .to_owned(); app.set_runner(move |mut app: App| { - app.initialize(); - let mut app_exit_event_reader = EventReader::::default(); match settings.run_mode { RunMode::Once => { diff --git a/crates/bevy_app/src/stage.rs b/crates/bevy_app/src/stage.rs index 1e4fa590528b7..fb8eb416307ee 100644 --- a/crates/bevy_app/src/stage.rs +++ b/crates/bevy_app/src/stage.rs @@ -1,11 +1,14 @@ +/// Name of the app stage that runs once at the beginning of the app +pub const STARTUP: &str = "startup"; + /// Name of app stage that runs before all other app stages pub const FIRST: &str = "first"; /// Name of app stage that runs before EVENT -pub const PRE_EVENT: &str = "pre_events"; +pub const PRE_EVENT: &str = "pre_event"; /// Name of app stage that updates events. Runs before UPDATE -pub const EVENT: &str = "events"; +pub const EVENT: &str = "event"; /// Name of app stage responsible for performing setup before an update. Runs before UPDATE. pub const PRE_UPDATE: &str = "pre_update"; diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index a7d8e008a000a..ef2dc16dc872a 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -13,6 +13,7 @@ mod path; pub use asset_server::*; pub use assets::*; +use bevy_ecs::SystemStage; use bevy_reflect::RegisterTypeBuilder; use bevy_tasks::IoTaskPool; pub use handle::*; @@ -73,14 +74,22 @@ impl Plugin for AssetPlugin { AssetServer::new(source, task_pool) }; - app.add_stage_before(bevy_app::stage::PRE_UPDATE, stage::LOAD_ASSETS) - .add_stage_after(bevy_app::stage::POST_UPDATE, stage::ASSET_EVENTS) - .add_resource(asset_server) - .register_type::() - .add_system_to_stage( - bevy_app::stage::PRE_UPDATE, - asset_server::free_unused_assets_system, - ); + app.add_stage_before( + bevy_app::stage::PRE_UPDATE, + stage::LOAD_ASSETS, + SystemStage::parallel(), + ) + .add_stage_after( + bevy_app::stage::POST_UPDATE, + stage::ASSET_EVENTS, + SystemStage::parallel(), + ) + .add_resource(asset_server) + .register_type::() + .add_system_to_stage( + bevy_app::stage::PRE_UPDATE, + asset_server::free_unused_assets_system, + ); #[cfg(all( feature = "filesystem_watcher", diff --git a/crates/bevy_core/src/lib.rs b/crates/bevy_core/src/lib.rs index 01339438be2f2..198def8ee5703 100644 --- a/crates/bevy_core/src/lib.rs +++ b/crates/bevy_core/src/lib.rs @@ -33,6 +33,7 @@ impl Plugin for CorePlugin { app.init_resource::