diff --git a/.gitignore b/.gitignore index 4fffb2f..91b8835 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target /Cargo.lock +.idea/ diff --git a/Cargo.toml b/Cargo.toml index cd69320..644b8df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ exclude = ["assets"] default = [ "fixedtimestep", "states", "bevy-compat", "app" ] fixedtimestep = [ "bevy_core", + "bevy_time" ] states = [ "bevy_utils", @@ -32,6 +33,7 @@ bevy_ecs = { git = "https://github.com/bevyengine/bevy" } bevy_app = { git = "https://github.com/bevyengine/bevy", optional = true } bevy_core = { git = "https://github.com/bevyengine/bevy", optional = true } bevy_utils = { git = "https://github.com/bevyengine/bevy", optional = true } +bevy_time = { git = "https://github.com/bevyengine/bevy", optional = true } [dev-dependencies] bevy = { git = "https://github.com/bevyengine/bevy" } diff --git a/examples/fixedtimestep.rs b/examples/fixedtimestep.rs index 693a109..2cbdd81 100644 --- a/examples/fixedtimestep.rs +++ b/examples/fixedtimestep.rs @@ -98,5 +98,5 @@ fn reposition_entities(mut q: Query<&mut Transform, With>) { } fn setup_camera(mut commands: Commands) { - commands.spawn_bundle(OrthographicCameraBundle::new_2d()); + commands.spawn_bundle(Camera2dBundle::default()); } diff --git a/examples/menu.rs b/examples/menu.rs index ef709c3..a1710d3 100644 --- a/examples/menu.rs +++ b/examples/menu.rs @@ -12,7 +12,7 @@ use bevy::prelude::*; use iyes_loopless::prelude::*; use bevy::app::AppExit; -use bevy::input::system::exit_on_esc_system; +use bevy::window::close_on_esc; use std::time::Duration; @@ -49,8 +49,6 @@ fn main() { .add_enter_system(GameState::MainMenu, setup_menu) // menu cleanup (state exit) systems .add_exit_system(GameState::MainMenu, despawn_with::) - // game setup (state enter) systems - .add_enter_system(GameState::InGame, setup_game_camera) // game cleanup (state exit) systems .add_exit_system(GameState::InGame, despawn_with::) .add_exit_system(GameState::InGame, despawn_with::) @@ -58,7 +56,7 @@ fn main() { .add_system_set( ConditionSet::new() .run_in_state(GameState::MainMenu) - .with_system(exit_on_esc_system) + .with_system(close_on_esc) .with_system(butt_interact_visual) // our menu button handlers .with_system(butt_exit.run_if(on_butt_interact::)) @@ -77,7 +75,7 @@ fn main() { // our other various systems: .add_system(debug_current_state) // setup our UI camera globally at startup and keep it alive at all times - .add_startup_system(setup_ui_camera) + .add_startup_system(setup_camera) .run(); } @@ -154,15 +152,9 @@ fn spawn_sprite(mut commands: Commands) { .insert(MySprite); } -/// Spawn the UI camera -fn setup_ui_camera(mut commands: Commands) { - commands.spawn_bundle(UiCameraBundle::default()); -} - -/// Spawn the game camera -fn setup_game_camera(mut commands: Commands) { - commands.spawn_bundle(OrthographicCameraBundle::new_2d()) - .insert(GameCamera); +/// Spawn the camera +fn setup_camera(mut commands: Commands) { + commands.spawn_bundle(Camera2dBundle::default()).insert(GameCamera); } /// Rotate all the sprites @@ -222,8 +214,8 @@ fn setup_menu(mut commands: Commands, ass: Res) { let butt_style = Style { justify_content: JustifyContent::Center, align_items: AlignItems::Center, - padding: Rect::all(Val::Px(8.0)), - margin: Rect::all(Val::Px(4.0)), + padding: UiRect::all(Val::Px(8.0)), + margin: UiRect::all(Val::Px(4.0)), flex_grow: 1.0, ..Default::default() }; @@ -238,7 +230,7 @@ fn setup_menu(mut commands: Commands, ass: Res) { color: UiColor(Color::rgb(0.5, 0.5, 0.5)), style: Style { size: Size::new(Val::Auto, Val::Auto), - margin: Rect::all(Val::Auto), + margin: UiRect::all(Val::Auto), align_self: AlignSelf::Center, flex_direction: FlexDirection::ColumnReverse, //align_items: AlignItems::Stretch, diff --git a/src/condition.rs b/src/condition.rs index 45020bf..2446808 100644 --- a/src/condition.rs +++ b/src/condition.rs @@ -662,12 +662,12 @@ impl ConditionSet { } pub fn before(mut self, label: impl AsSystemLabel + 'static) -> Self { - self.labellers.push(Box::new(move |set: SystemSet| set.before(label.as_system_label()))); + self.labellers.push(Box::new(move |set: SystemSet| set.before(label))); self } pub fn after(mut self, label: impl AsSystemLabel + 'static) -> Self { - self.labellers.push(Box::new(move |set: SystemSet| set.after(label.as_system_label()))); + self.labellers.push(Box::new(move |set: SystemSet| set.after(label))); self } } diff --git a/src/fixedtimestep.rs b/src/fixedtimestep.rs index d712a6b..f26b9a0 100644 --- a/src/fixedtimestep.rs +++ b/src/fixedtimestep.rs @@ -1,6 +1,6 @@ use std::time::Duration; +use bevy_time::Time; -use bevy_core::Time; use bevy_ecs::prelude::*; /// This type will be available as a resource, while a fixed timestep stage diff --git a/src/lib.rs b/src/lib.rs index c21294d..02c0c2c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,8 +8,9 @@ pub mod prelude { pub use crate::condition::{ConditionHelpers, IntoConditionalSystem, ConditionSet, AddConditionalToSet}; #[cfg(feature = "fixedtimestep")] pub use crate::fixedtimestep::{FixedTimestepInfo, FixedTimestepStage}; - #[cfg(feature = "states")] - pub use crate::state::{CurrentState, NextState, StateTransitionStage}; #[cfg(feature = "app")] pub use crate::state::app::AppLooplessStateExt; + pub use crate::state::schedule::ScheduleLooplessStateExt; + #[cfg(feature = "states")] + pub use crate::state::{CurrentState, NextState, StateTransitionStage}; } diff --git a/src/state.rs b/src/state.rs index d95cb22..8e48f82 100644 --- a/src/state.rs +++ b/src/state.rs @@ -228,9 +228,16 @@ pub mod app { use super::StateTransitionStage; - #[derive(Debug, Clone, PartialEq, Eq, Hash, StageLabel)] + #[derive(Debug, Clone)] pub struct StateTransitionStageLabel(TypeId, String); + impl StageLabel for StateTransitionStageLabel { + fn as_str(&self) -> &'static str { + let s = format!("{:?}{}", self.0, self.1); + Box::leak(s.into_boxed_str()) + } + } + impl StateTransitionStageLabel { pub fn from_type() -> Self { use std::any::type_name; @@ -303,37 +310,156 @@ pub mod app { } fn add_enter_system(&mut self, state: T, system: impl IntoSystemDescriptor) -> &mut App { let stage = self.schedule.get_stage_mut::>(&StateTransitionStageLabel::from_type::()) - .expect("State Transiton Stage not found (assuming auto-added label)"); + .expect("State Transition Stage not found (assuming auto-added label)"); stage.add_enter_system(state, system); self } fn add_exit_system(&mut self, state: T, system: impl IntoSystemDescriptor) -> &mut App { let stage = self.schedule.get_stage_mut::>(&StateTransitionStageLabel::from_type::()) - .expect("State Transiton Stage not found (assuming auto-added label)"); + .expect("State Transition Stage not found (assuming auto-added label)"); stage.add_exit_system(state, system); self } fn add_enter_system_set(&mut self, state: T, system_set: SystemSet) -> &mut App { let stage = self.schedule.get_stage_mut::>(&StateTransitionStageLabel::from_type::()) - .expect("State Transiton Stage not found (assuming auto-added label)"); + .expect("State Transition Stage not found (assuming auto-added label)"); stage.add_enter_system_set(state, system_set); self } fn add_exit_system_set(&mut self, state: T, system_set: SystemSet) -> &mut App { let stage = self.schedule.get_stage_mut::>(&StateTransitionStageLabel::from_type::()) - .expect("State Transiton Stage not found (assuming auto-added label)"); + .expect("State Transition Stage not found (assuming auto-added label)"); stage.add_exit_system_set(state, system_set); self } fn set_enter_stage(&mut self, state: T, enter_stage: impl Stage) -> &mut App { let stage = self.schedule.get_stage_mut::>(&StateTransitionStageLabel::from_type::()) - .expect("State Transiton Stage not found (assuming auto-added label)"); + .expect("State Transition Stage not found (assuming auto-added label)"); stage.set_enter_stage(state, enter_stage); self } fn set_exit_stage(&mut self, state: T, exit_stage: impl Stage) -> &mut App { let stage = self.schedule.get_stage_mut::>(&StateTransitionStageLabel::from_type::()) - .expect("State Transiton Stage not found (assuming auto-added label)"); + .expect("State Transition Stage not found (assuming auto-added label)"); + stage.set_exit_stage(state, exit_stage); + self + } + } +} + +pub mod schedule { + use std::any::TypeId; + + use bevy_ecs::schedule::{StageLabel, Stage, StateData, IntoSystemDescriptor, SystemSet, Schedule}; + + use super::StateTransitionStage; + + #[derive(Debug, Clone)] + pub struct StateTransitionStageLabel(TypeId, String); + + impl StageLabel for StateTransitionStageLabel { + fn as_str(&self) -> &'static str { + let s = format!("{:?}{}", self.0, self.1); + Box::leak(s.into_boxed_str()) + } + } + + impl StateTransitionStageLabel { + pub fn from_type() -> Self { + use std::any::type_name; + StateTransitionStageLabel(TypeId::of::(), type_name::().to_owned()) + } + } + + pub trait ScheduleLooplessStateExt { + /// Add a `StateTransitionStage` after the specified stage + fn add_loopless_state_after_stage(&mut self, stage: impl StageLabel, init: T) -> &mut Schedule; + /// Add a `StateTransitionStage` before the specified stage + fn add_loopless_state_before_stage(&mut self, stage: impl StageLabel, init: T) -> &mut Schedule; + /// Add an enter system for the given state + /// + /// Requires the stage to be labeled with a `StateTransitionStageLabel` + /// (as done by the `add_loopless_state*` methods). + fn add_enter_system(&mut self, state: T, system: impl IntoSystemDescriptor) -> &mut Schedule; + /// Add an exit system for the given state + /// + /// Requires the stage to be labeled with a `StateTransitionStageLabel` + /// (as done by the `add_loopless_state*` methods). + fn add_exit_system(&mut self, state: T, system: impl IntoSystemDescriptor) -> &mut Schedule; + /// Add an enter system set for the given state + /// + /// Requires the stage to be labeled with a `StateTransitionStageLabel` + /// (as done by the `add_loopless_state*` methods). + fn add_enter_system_set(&mut self, state: T, system_set: SystemSet) -> &mut Schedule; + /// Add an exit system set for the given state + /// + /// Requires the stage to be labeled with a `StateTransitionStageLabel` + /// (as done by the `add_loopless_state*` methods). + fn add_exit_system_set(&mut self, state: T, system_set: SystemSet) -> &mut Schedule; + /// Add a custom stage to execute for the given state + /// + /// Requires the stage to be labeled with a `StateTransitionStageLabel` + /// (as done by the `add_loopless_state*` methods). + /// + /// Cannot be used together with `add_enter_system`. + fn set_enter_stage(&mut self, state: T, system: impl Stage) -> &mut Schedule; + /// Add a custom stage to execute for the given state + /// + /// Requires the stage to be labeled with a `StateTransitionStageLabel` + /// (as done by the `add_loopless_state*` methods). + /// + /// Cannot be used together with `add_enter_system`. + fn set_exit_stage(&mut self, state: T, system: impl Stage) -> &mut Schedule; + } + + impl ScheduleLooplessStateExt for Schedule { + fn add_loopless_state_after_stage(&mut self, stage: impl StageLabel, init: T) -> &mut Schedule { + self.add_stage_after( + stage, + StateTransitionStageLabel::from_type::(), + StateTransitionStage::new(init) + ) + } + fn add_loopless_state_before_stage(&mut self, stage: impl StageLabel, init: T) -> &mut Schedule { + self.add_stage_before( + stage, + StateTransitionStageLabel::from_type::(), + StateTransitionStage::new(init) + ) + } + fn add_enter_system(&mut self, state: T, system: impl IntoSystemDescriptor) -> &mut Schedule { + let stage = self.get_stage_mut::>(&StateTransitionStageLabel::from_type::()) + .expect("State Transition Stage not found (assuming auto-added label)"); + stage.add_enter_system(state, system); + self + } + fn add_exit_system(&mut self, state: T, system: impl IntoSystemDescriptor) -> &mut Schedule { + let stage = self.get_stage_mut::>(&StateTransitionStageLabel::from_type::()) + .expect("State Transition Stage not found (assuming auto-added label)"); + stage.add_exit_system(state, system); + self + } + fn add_enter_system_set(&mut self, state: T, system_set: SystemSet) -> &mut Schedule { + let stage = self.get_stage_mut::>(&StateTransitionStageLabel::from_type::()) + .expect("State Transition Stage not found (assuming auto-added label)"); + stage.add_enter_system_set(state, system_set); + self + } + fn add_exit_system_set(&mut self, state: T, system_set: SystemSet) -> &mut Schedule { + let stage = self.get_stage_mut::>(&StateTransitionStageLabel::from_type::()) + .expect("State Transition Stage not found (assuming auto-added label)"); + stage.add_exit_system_set(state, system_set); + self + } + fn set_enter_stage(&mut self, state: T, enter_stage: impl Stage) -> &mut Schedule { + let stage = self.get_stage_mut::>(&StateTransitionStageLabel::from_type::()) + .expect("State Transition Stage not found (assuming auto-added label)"); + stage.set_enter_stage(state, enter_stage); + self + } + fn set_exit_stage(&mut self, state: T, exit_stage: impl Stage) -> &mut Schedule { + let stage = self.get_stage_mut::>(&StateTransitionStageLabel::from_type::()) + .expect("State Transition Stage not found (assuming auto-added label)"); stage.set_exit_stage(state, exit_stage); self }