Skip to content

Commit 0824812

Browse files
authored
Example time api (#10204)
# Objective - Fixes #10133 ## Solution - Add a new example that focuses on using `Virtual` time ## Changelog ### Added - new `virtual_time` example ### Changed - moved `time` & `timers` examples to the new `examples/time` folder
1 parent 3eaad94 commit 0824812

File tree

5 files changed

+243
-6
lines changed

5 files changed

+243
-6
lines changed

Cargo.toml

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1420,27 +1420,39 @@ description = "Illustrates creating custom system parameters with `SystemParam`"
14201420
category = "ECS (Entity Component System)"
14211421
wasm = false
14221422

1423+
# Time
14231424
[[example]]
14241425
name = "time"
1425-
path = "examples/ecs/time.rs"
1426+
path = "examples/time/time.rs"
14261427

14271428
[package.metadata.example.time]
14281429
name = "Time handling"
14291430
description = "Explains how Time is handled in ECS"
1430-
category = "ECS (Entity Component System)"
1431+
category = "Time"
1432+
wasm = false
1433+
1434+
[[example]]
1435+
name = "virtual_time"
1436+
path = "examples/time/virtual_time.rs"
1437+
1438+
[package.metadata.example.virtual_time]
1439+
name = "Virtual time"
1440+
description = "Shows how `Time<Virtual>` can be used to pause, resume, slow down and speed up a game."
1441+
category = "Time"
14311442
wasm = false
14321443

14331444
[[example]]
14341445
name = "timers"
1435-
path = "examples/ecs/timers.rs"
1446+
path = "examples/time/timers.rs"
14361447
doc-scrape-examples = true
14371448

14381449
[package.metadata.example.timers]
14391450
name = "Timers"
14401451
description = "Illustrates ticking `Timer` resources inside systems and handling their state"
1441-
category = "ECS (Entity Component System)"
1452+
category = "Time"
14421453
wasm = false
14431454

1455+
14441456
# Games
14451457
[[example]]
14461458
name = "alien_cake_addict"

examples/README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ git checkout v0.4.0
5353
- [Scene](#scene)
5454
- [Shaders](#shaders)
5555
- [Stress Tests](#stress-tests)
56+
- [Time](#time)
5657
- [Tools](#tools)
5758
- [Transforms](#transforms)
5859
- [UI (User Interface)](#ui-user-interface)
@@ -233,8 +234,6 @@ Example | Description
233234
[System Closure](../examples/ecs/system_closure.rs) | Show how to use closures as systems, and how to configure `Local` variables by capturing external state
234235
[System Parameter](../examples/ecs/system_param.rs) | Illustrates creating custom system parameters with `SystemParam`
235236
[System Piping](../examples/ecs/system_piping.rs) | Pipe the output of one system into a second, allowing you to handle any errors gracefully
236-
[Time handling](../examples/ecs/time.rs) | Explains how Time is handled in ECS
237-
[Timers](../examples/ecs/timers.rs) | Illustrates ticking `Timer` resources inside systems and handling their state
238237

239238
## Games
240239

@@ -326,6 +325,14 @@ Example | Description
326325
[Text Pipeline](../examples/stress_tests/text_pipeline.rs) | Text Pipeline benchmark
327326
[Transform Hierarchy](../examples/stress_tests/transform_hierarchy.rs) | Various test cases for hierarchy and transform propagation performance
328327

328+
## Time
329+
330+
Example | Description
331+
--- | ---
332+
[Time handling](../examples/time/time.rs) | Explains how Time is handled in ECS
333+
[Timers](../examples/time/timers.rs) | Illustrates ticking `Timer` resources inside systems and handling their state
334+
[Virtual time](../examples/time/virtual_time.rs) | Shows how `Time<Virtual>` can be used to pause, resume, slow down and speed up a game.
335+
329336
## Tools
330337

331338
Example | Description
File renamed without changes.
File renamed without changes.

examples/time/virtual_time.rs

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
//! Shows how `Time<Virtual>` can be used to pause, resume, slow down
2+
//! and speed up a game.
3+
4+
use std::time::Duration;
5+
6+
use bevy::{
7+
input::common_conditions::input_just_pressed, prelude::*,
8+
time::common_conditions::on_real_timer,
9+
};
10+
11+
fn main() {
12+
App::new()
13+
.add_plugins(DefaultPlugins)
14+
.add_systems(Startup, setup)
15+
.add_systems(
16+
Update,
17+
(
18+
move_virtual_time_sprites,
19+
move_real_time_sprites,
20+
toggle_pause.run_if(input_just_pressed(KeyCode::Space)),
21+
change_time_speed::<1>.run_if(input_just_pressed(KeyCode::Up)),
22+
change_time_speed::<-1>.run_if(input_just_pressed(KeyCode::Down)),
23+
(update_virtual_time_info_text, update_real_time_info_text)
24+
// update the texts on a timer to make them more readable
25+
// `on_timer` run condition uses `Virtual` time meaning it's scaled
26+
// and would result in the UI updating at different intervals based
27+
// on `Time<Virtual>::relative_speed` and `Time<Virtual>::is_paused()`
28+
.run_if(on_real_timer(Duration::from_millis(250))),
29+
),
30+
)
31+
.run();
32+
}
33+
34+
/// `Real` time related marker
35+
#[derive(Component)]
36+
struct RealTime;
37+
38+
/// `Virtual` time related marker
39+
#[derive(Component)]
40+
struct VirtualTime;
41+
42+
/// Setup the example
43+
fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut time: ResMut<Time<Virtual>>) {
44+
// start with double `Virtual` time resulting in one of the sprites moving at twice the speed
45+
// of the other sprite which moves based on `Real` (unscaled) time
46+
time.set_relative_speed(2.);
47+
48+
commands.spawn(Camera2dBundle::default());
49+
50+
let virtual_color = Color::GOLD;
51+
let sprite_scale = Vec2::splat(0.5).extend(1.);
52+
let texture_handle = asset_server.load("branding/icon.png");
53+
54+
// the sprite moving based on real time
55+
commands.spawn((
56+
SpriteBundle {
57+
texture: texture_handle.clone(),
58+
transform: Transform::from_scale(sprite_scale),
59+
..default()
60+
},
61+
RealTime,
62+
));
63+
64+
// the sprite moving based on virtual time
65+
commands.spawn((
66+
SpriteBundle {
67+
texture: texture_handle,
68+
sprite: Sprite {
69+
color: virtual_color,
70+
..default()
71+
},
72+
transform: Transform {
73+
scale: sprite_scale,
74+
translation: Vec3::new(0., -160., 0.),
75+
..default()
76+
},
77+
..default()
78+
},
79+
VirtualTime,
80+
));
81+
82+
// info UI
83+
let font_size = 40.;
84+
85+
commands
86+
.spawn(NodeBundle {
87+
style: Style {
88+
display: Display::Flex,
89+
justify_content: JustifyContent::SpaceBetween,
90+
width: Val::Percent(100.),
91+
position_type: PositionType::Absolute,
92+
top: Val::Px(0.),
93+
padding: UiRect::all(Val::Px(20.0)),
94+
..default()
95+
},
96+
..default()
97+
})
98+
.with_children(|builder| {
99+
// real time info
100+
builder.spawn((
101+
TextBundle::from_section(
102+
"",
103+
TextStyle {
104+
font_size,
105+
..default()
106+
},
107+
),
108+
RealTime,
109+
));
110+
111+
// keybindings
112+
builder.spawn(
113+
TextBundle::from_section(
114+
"CONTROLS\nUn/Pause: Space\nSpeed+: Up\nSpeed-: Down",
115+
TextStyle {
116+
font_size,
117+
color: Color::rgb(0.85, 0.85, 0.85),
118+
..default()
119+
},
120+
)
121+
.with_text_alignment(TextAlignment::Center),
122+
);
123+
124+
// virtual time info
125+
builder.spawn((
126+
TextBundle::from_section(
127+
"",
128+
TextStyle {
129+
font_size,
130+
color: virtual_color,
131+
..default()
132+
},
133+
)
134+
.with_text_alignment(TextAlignment::Right),
135+
VirtualTime,
136+
));
137+
});
138+
}
139+
140+
/// Move sprites using `Real` (unscaled) time
141+
fn move_real_time_sprites(
142+
mut sprite_query: Query<&mut Transform, (With<Sprite>, With<RealTime>)>,
143+
// `Real` time which is not scaled or paused
144+
time: Res<Time<Real>>,
145+
) {
146+
for mut transform in sprite_query.iter_mut() {
147+
// move roughly half the screen in a `Real` second
148+
// when the time is scaled the speed is going to change
149+
// and the sprite will stay still the the time is paused
150+
transform.translation.x = get_sprite_translation_x(time.elapsed_seconds());
151+
}
152+
}
153+
154+
/// Move sprites using `Virtual` (scaled) time
155+
fn move_virtual_time_sprites(
156+
mut sprite_query: Query<&mut Transform, (With<Sprite>, With<VirtualTime>)>,
157+
// the default `Time` is either `Time<Virtual>` in regular systems
158+
// or `Time<Fixed>` in fixed timestep systems so `Time::delta()`,
159+
// `Time::elapsed()` will return the appropriate values either way
160+
time: Res<Time>,
161+
) {
162+
for mut transform in sprite_query.iter_mut() {
163+
// move roughly half the screen in a `Virtual` second
164+
// when time is scaled using `Time<Virtual>::set_relative_speed` it's going
165+
// to move at a different pace and the sprite will stay still when time is
166+
// `Time<Virtual>::is_paused()`
167+
transform.translation.x = get_sprite_translation_x(time.elapsed_seconds());
168+
}
169+
}
170+
171+
fn get_sprite_translation_x(elapsed: f32) -> f32 {
172+
elapsed.sin() * 500.
173+
}
174+
175+
/// Update the speed of `Time<Virtual>.` by `DELTA`
176+
fn change_time_speed<const DELTA: i8>(mut time: ResMut<Time<Virtual>>) {
177+
let time_speed = (time.relative_speed() + DELTA as f32)
178+
.round()
179+
.clamp(0.25, 5.);
180+
181+
// set the speed of the virtual time to speed it up or slow it down
182+
time.set_relative_speed(time_speed);
183+
}
184+
185+
/// pause or resume `Relative` time
186+
fn toggle_pause(mut time: ResMut<Time<Virtual>>) {
187+
if time.is_paused() {
188+
time.unpause();
189+
} else {
190+
time.pause();
191+
}
192+
}
193+
194+
/// Update the `Real` time info text
195+
fn update_real_time_info_text(time: Res<Time<Real>>, mut query: Query<&mut Text, With<RealTime>>) {
196+
for mut text in &mut query {
197+
text.sections[0].value = format!(
198+
"REAL TIME\nElapsed: {:.1}\nDelta: {:.5}\n",
199+
time.elapsed_seconds(),
200+
time.delta_seconds(),
201+
);
202+
}
203+
}
204+
205+
/// Update the `Virtual` time info text
206+
fn update_virtual_time_info_text(
207+
time: Res<Time<Virtual>>,
208+
mut query: Query<&mut Text, With<VirtualTime>>,
209+
) {
210+
for mut text in &mut query {
211+
text.sections[0].value = format!(
212+
"VIRTUAL TIME\nElapsed: {:.1}\nDelta: {:.5}\nSpeed: {:.2}",
213+
time.elapsed_seconds(),
214+
time.delta_seconds(),
215+
time.relative_speed()
216+
);
217+
}
218+
}

0 commit comments

Comments
 (0)