//! Shows how `Time` can be used to pause, resume, slow down //! and speed up a game. use std::time::Duration; use bevy::{ color::palettes::css::*, 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(Camera2d); let virtual_color = GOLD.into(); 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(( Sprite::from_image(texture_handle.clone()), Transform::from_scale(sprite_scale), RealTime, )); // the sprite moving based on virtual time commands.spawn(( Sprite { image: texture_handle, color: virtual_color, ..Default::default() }, Transform { scale: sprite_scale, translation: Vec3::new(0., -160., 0.), ..default() }, VirtualTime, )); // info UI let font_size = 33.; commands .spawn(Node { 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() }) .with_children(|builder| { // real time info builder.spawn(( Text::default(), TextFont { font_size, ..default() }, RealTime, )); // keybindings builder.spawn(( Text::new("CONTROLS\nUn/Pause: Space\nSpeed+: Up\nSpeed-: Down"), TextFont { font_size, ..default() }, TextColor(Color::srgb(0.85, 0.85, 0.85)), TextLayout::new_with_justify(JustifyText::Center), )); // virtual time info builder.spawn(( Text::default(), TextFont { font_size, ..default() }, TextColor(virtual_color), TextLayout::new_with_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 time is paused transform.translation.x = get_sprite_translation_x(time.elapsed_secs()); } } /// 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