diff --git a/Cargo.toml b/Cargo.toml index 745a02e02df61..c11974ca292bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,7 +71,11 @@ bevy_asset = ["bevy_internal/bevy_asset"] bevy_audio = ["bevy_internal/bevy_audio"] # Provides cameras and other basic render pipeline features -bevy_core_pipeline = ["bevy_internal/bevy_core_pipeline", "bevy_asset", "bevy_render"] +bevy_core_pipeline = [ + "bevy_internal/bevy_core_pipeline", + "bevy_asset", + "bevy_render", +] # Plugin for dynamic loading (using [libloading](https://crates.io/crates/libloading)) bevy_dynamic_plugin = ["bevy_internal/bevy_dynamic_plugin"] @@ -83,7 +87,12 @@ bevy_gilrs = ["bevy_internal/bevy_gilrs"] bevy_gltf = ["bevy_internal/bevy_gltf", "bevy_asset", "bevy_scene", "bevy_pbr"] # Adds PBR rendering -bevy_pbr = ["bevy_internal/bevy_pbr", "bevy_asset", "bevy_render", "bevy_core_pipeline"] +bevy_pbr = [ + "bevy_internal/bevy_pbr", + "bevy_asset", + "bevy_render", + "bevy_core_pipeline", +] # Provides rendering functionality bevy_render = ["bevy_internal/bevy_render"] @@ -98,7 +107,12 @@ bevy_sprite = ["bevy_internal/bevy_sprite", "bevy_render", "bevy_core_pipeline"] bevy_text = ["bevy_internal/bevy_text", "bevy_asset", "bevy_sprite"] # A custom ECS-driven UI framework -bevy_ui = ["bevy_internal/bevy_ui", "bevy_core_pipeline", "bevy_text", "bevy_sprite"] +bevy_ui = [ + "bevy_internal/bevy_ui", + "bevy_core_pipeline", + "bevy_text", + "bevy_sprite", +] # winit window and input backend bevy_winit = ["bevy_internal/bevy_winit"] @@ -113,7 +127,11 @@ trace_chrome = ["trace", "bevy_internal/trace_chrome"] trace_tracy = ["trace", "bevy_internal/trace_tracy"] # Tracing support, with memory profiling, exposing a port for Tracy -trace_tracy_memory = ["trace", "bevy_internal/trace_tracy", "bevy_internal/trace_tracy_memory"] +trace_tracy_memory = [ + "trace", + "bevy_internal/trace_tracy", + "bevy_internal/trace_tracy_memory", +] # Tracing support trace = ["bevy_internal/trace"] @@ -1398,27 +1416,39 @@ description = "Illustrates creating custom system parameters with `SystemParam`" category = "ECS (Entity Component System)" wasm = false +# Time [[example]] name = "time" -path = "examples/ecs/time.rs" +path = "examples/time/time.rs" [package.metadata.example.time] name = "Time handling" description = "Explains how Time is handled in ECS" -category = "ECS (Entity Component System)" +category = "Time" +wasm = false + +[[example]] +name = "virtual_time" +path = "examples/time/virtual_time.rs" + +[package.metadata.example.virtual_time] +name = "Virtual time" +description = "Shows how `Time` can be used to pause, resume, slow down and speed up a game." +category = "Time" wasm = false [[example]] name = "timers" -path = "examples/ecs/timers.rs" +path = "examples/time/timers.rs" doc-scrape-examples = true [package.metadata.example.timers] name = "Timers" description = "Illustrates ticking `Timer` resources inside systems and handling their state" -category = "ECS (Entity Component System)" +category = "Time" wasm = false + # Games [[example]] name = "alien_cake_addict" diff --git a/examples/README.md b/examples/README.md index fde389848053d..a24c77604afce 100644 --- a/examples/README.md +++ b/examples/README.md @@ -53,6 +53,7 @@ git checkout v0.4.0 - [Scene](#scene) - [Shaders](#shaders) - [Stress Tests](#stress-tests) + - [Time](#time) - [Tools](#tools) - [Transforms](#transforms) - [UI (User Interface)](#ui-user-interface) @@ -233,8 +234,6 @@ Example | Description [System Closure](../examples/ecs/system_closure.rs) | Show how to use closures as systems, and how to configure `Local` variables by capturing external state [System Parameter](../examples/ecs/system_param.rs) | Illustrates creating custom system parameters with `SystemParam` [System Piping](../examples/ecs/system_piping.rs) | Pipe the output of one system into a second, allowing you to handle any errors gracefully -[Time handling](../examples/ecs/time.rs) | Explains how Time is handled in ECS -[Timers](../examples/ecs/timers.rs) | Illustrates ticking `Timer` resources inside systems and handling their state ## Games @@ -326,6 +325,14 @@ Example | Description [Text Pipeline](../examples/stress_tests/text_pipeline.rs) | Text Pipeline benchmark [Transform Hierarchy](../examples/stress_tests/transform_hierarchy.rs) | Various test cases for hierarchy and transform propagation performance +## Time + +Example | Description +--- | --- +[Time handling](../examples/time/time.rs) | Explains how Time is handled in ECS +[Timers](../examples/time/timers.rs) | Illustrates ticking `Timer` resources inside systems and handling their state +[Virtual time](../examples/time/virtual_time.rs) | Shows how `Time` can be used to pause, resume, slow down and speed up a game. + ## Tools Example | Description diff --git a/examples/ecs/time.rs b/examples/time/time.rs similarity index 100% rename from examples/ecs/time.rs rename to examples/time/time.rs diff --git a/examples/ecs/timers.rs b/examples/time/timers.rs similarity index 100% rename from examples/ecs/timers.rs rename to examples/time/timers.rs diff --git a/examples/time/virtual_time.rs b/examples/time/virtual_time.rs new file mode 100644 index 0000000000000..bcc41507ec18a --- /dev/null +++ b/examples/time/virtual_time.rs @@ -0,0 +1,218 @@ +//! 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::Up)), + change_time_speed::<-1>.run_if(input_just_pressed(KeyCode::Down)), + (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_alignment(TextAlignment::Center), + ); + + // virtual time info + builder.spawn(( + TextBundle::from_section( + "", + TextStyle { + font_size, + color: virtual_color, + ..default() + }, + ) + .with_text_alignment(TextAlignment::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