//! Complex example showcasing all the features together. //! //! Shows how our states, fixed timestep, and custom run conditions, can all be used together! //! //! Also shows how run conditions could be helpful for Bevy UI button handling! //! //! This example has a main menu with two buttons: exit the app and enter the game. //! //! How to "play the game": hold spacebar to spawn colorful squares, release spacebar to make them spin! <3 use bevy::prelude::*; use iyes_loopless::prelude::*; use bevy::app::AppExit; use bevy::window::close_on_esc; use std::time::Duration; use rand::prelude::*; /// Our Application State #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] enum GameState { MainMenu, InGame, } fn main() { App::new() .add_plugins(DefaultPlugins) // add out states driver .add_loopless_state(GameState::MainMenu) // Add a FixedTimestep, cuz we can! .add_fixed_timestep( Duration::from_millis(125), // give it a label "my_fixed_update", ) // menu setup (state enter) systems .add_enter_system(GameState::MainMenu, setup_menu) // menu cleanup (state exit) systems .add_exit_system(GameState::MainMenu, despawn_with::) // game cleanup (state exit) systems .add_exit_system(GameState::InGame, despawn_with::) // menu stuff .add_system_set( ConditionSet::new() .run_in_state(GameState::MainMenu) .with_system(close_on_esc) .with_system(butt_interact_visual) // our menu button handlers .with_system(butt_exit.run_if(on_butt_interact::)) .with_system(butt_game.run_if(on_butt_interact::)) .into() ) // in-game stuff .add_system_set( ConditionSet::new() .run_in_state(GameState::InGame) .with_system(back_to_menu_on_esc) .with_system(clear_on_del) .with_system(spin_sprites.run_if_not(spacebar_pressed)) .into() ) .add_fixed_timestep_system( "my_fixed_update", 0, spawn_sprite // only in-game! .run_in_state(GameState::InGame) // only while the spacebar is pressed .run_if(spacebar_pressed) ) // our other various systems: .add_system(debug_current_state) // setup our camera globally (for UI) at startup and keep it alive at all times .add_startup_system(setup_camera) .run(); } /// Marker for our in-game sprites #[derive(Component)] struct MySprite; /// Marker for the main menu entity #[derive(Component)] struct MainMenu; /// Marker for the main game camera entity #[derive(Component)] struct GameCamera; /// Marker for the "Exit App" button #[derive(Component)] struct ExitButt; /// Marker for the "Enter Game" button #[derive(Component)] struct EnterButt; /// Reset the in-game state when pressing delete fn clear_on_del(mut commands: Commands, kbd: Res>) { if kbd.just_pressed(KeyCode::Delete) || kbd.just_pressed(KeyCode::Back) { commands.insert_resource(NextState(GameState::InGame)); } } /// Transition back to menu on pressing Escape fn back_to_menu_on_esc(mut commands: Commands, kbd: Res>) { if kbd.just_pressed(KeyCode::Escape) { commands.insert_resource(NextState(GameState::MainMenu)); } } /// We can just access the `CurrentState`, and even use change detection! fn debug_current_state(state: Res>) { if state.is_changed() { println!("Detected state change to {:?}!", state); } } /// Condition system for holding the space bar fn spacebar_pressed(kbd: Res>) -> bool { kbd.pressed(KeyCode::Space) } /// Despawn all entities with a given component type fn despawn_with(mut commands: Commands, q: Query>) { for e in q.iter() { commands.entity(e).despawn_recursive(); } } /// Spawn a MySprite entity fn spawn_sprite(mut commands: Commands) { let mut rng = thread_rng(); commands.spawn((SpriteBundle { sprite: Sprite { color: Color::rgba(rng.gen(), rng.gen(), rng.gen(), 0.5), custom_size: Some(Vec2::new(64., 64.)), ..Default::default() }, transform: Transform::from_xyz( rng.gen_range(-420.0..420.0), rng.gen_range(-420.0..420.0), rng.gen_range(0.0..100.0), ), ..Default::default() }, MySprite)); } /// Spawn the camera fn setup_camera(mut commands: Commands) { commands.spawn((Camera2dBundle::default(), GameCamera)); } /// Rotate all the sprites fn spin_sprites(mut q: Query<&mut Transform, With>, t: Res