//! Shows how `Time` can be used to pause, resume, slow down //! and speed up a game. use std::time::Duration; use bevy::{ input::common_conditions::input_just_pressed, prelude::*, time::common_conditions::on_real_timer, }; fn main() { App::new() .add_plugins(DefaultPlugins) .add_systems(Startup, setup) .add_systems( Update, ( move_virtual_time_sprites, move_real_time_sprites, toggle_pause.run_if(input_just_pressed(KeyCode::Space)), change_time_speed::<1>.run_if(input_just_pressed(KeyCode::ArrowUp)), change_time_speed::<-1>.run_if(input_just_pressed(KeyCode::ArrowDown)), (update_virtual_time_info_text, update_real_time_info_text) // update the texts on a timer to make them more readable // `on_timer` run condition uses `Virtual` time meaning it's scaled // and would result in the UI updating at different intervals based // on `Time::relative_speed` and `Time::is_paused()` .run_if(on_real_timer(Duration::from_millis(250))), ), ) .run(); } /// `Real` time related marker #[derive(Component)] struct RealTime; /// `Virtual` time related marker #[derive(Component)] struct VirtualTime; /// Setup the example fn setup(mut commands: Commands, asset_server: Res, mut time: ResMut>) { // start with double `Virtual` time resulting in one of the sprites moving at twice the speed // of the other sprite which moves based on `Real` (unscaled) time time.set_relative_speed(2.); commands.spawn(Camera2dBundle::default()); let virtual_color = Color::GOLD; let sprite_scale = Vec2::splat(0.5).extend(1.); let texture_handle = asset_server.load("branding/icon.png"); // the sprite moving based on real time commands.spawn(( SpriteBundle { texture: texture_handle.clone(), transform: Transform::from_scale(sprite_scale), ..default() }, RealTime, )); // the sprite moving based on virtual time commands.spawn(( SpriteBundle { texture: texture_handle, sprite: Sprite { color: virtual_color, ..default() }, transform: Transform { scale: sprite_scale, translation: Vec3::new(0., -160., 0.), ..default() }, ..default() }, VirtualTime, )); // info UI let font_size = 40.; commands .spawn(NodeBundle { style: Style { display: Display::Flex, justify_content: JustifyContent::SpaceBetween, width: Val::Percent(100.), position_type: PositionType::Absolute, top: Val::Px(0.), padding: UiRect::all(Val::Px(20.0)), ..default() }, ..default() }) .with_children(|builder| { // real time info builder.spawn(( TextBundle::from_section( "", TextStyle { font_size, ..default() }, ), RealTime, )); // keybindings builder.spawn( TextBundle::from_section( "CONTROLS\nUn/Pause: Space\nSpeed+: Up\nSpeed-: Down", TextStyle { font_size, color: Color::rgb(0.85, 0.85, 0.85), ..default() }, ) .with_text_justify(JustifyText::Center), ); // virtual time info builder.spawn(( TextBundle::from_section( "", TextStyle { font_size, color: virtual_color, ..default() }, ) .with_text_justify(JustifyText::Right), VirtualTime, )); }); } /// Move sprites using `Real` (unscaled) time fn move_real_time_sprites( mut sprite_query: Query<&mut Transform, (With, With)>, // `Real` time which is not scaled or paused time: Res>, ) { for mut transform in sprite_query.iter_mut() { // move roughly half the screen in a `Real` second // when the time is scaled the speed is going to change // and the sprite will stay still the the time is paused transform.translation.x = get_sprite_translation_x(time.elapsed_seconds()); } } /// Move sprites using `Virtual` (scaled) time fn move_virtual_time_sprites( mut sprite_query: Query<&mut Transform, (With, With)>, // the default `Time` is either `Time` in regular systems // or `Time` in fixed timestep systems so `Time::delta()`, // `Time::elapsed()` will return the appropriate values either way time: Res