mirror of
https://github.com/bevyengine/bevy.git
synced 2026-05-06 06:06:42 -04:00
1fdf4267ae
# Objective - A generalized mechanism for "doing something later" is desirable for many games, especially when it comes to gameplay logic and VFX. - Fixes https://github.com/bevyengine/bevy/issues/15129 - Closes #20155 ## Solution - Build off the work in https://github.com/bevyengine/bevy/pull/20155#issuecomment-3702483127, especially @laundmo's comment. - Add a `DelayedCommands` helper obtainable via `commands.delayed()` that owns `CommandQueue`s and hands out new `Commands` bound to them. - When the `DelayedCommands` helper is dropped, push spawn commands onto the host `Commands` to spawn the queues as `DelayedCommandQueue` entities. - The entities are ticked by a new system added by `TimePlugin`. When the timer fires, the queue is submitted onto that system's `Commands`. ## Testing - Added a new test in `bevy_time` and it seems to work. - I'm not very familiar with doing hacky things like using `Drop` like this and would therefore appreciate careful review and guidance if changes are requested. --- ## Showcase ```rust fn my_cool_system(mut commands: Commands) { // fairly unobtrusive one-line delayed spawn commands.delayed().secs(0.1).spawn(DummyComponent); // the DelayedCommands can be stored to reuse more tersely let mut delayed = commands.delayed(); // allocation happens immediately so you can even queue // further operations on entities that aren't spawned yet let entity = delayed.secs(0.5).spawn_empty().id(); delayed.secs(0.7).entity(entity).insert(DummyComponent); // `delayed.secs` and `delayed.duration` both simply return a // `Commands` rebound to the stored `CommandQueue`, so you can additionally // just store that and reuse it to queue multiple commands with the same delay let mut in_1_sec = delayed.duration(Duration::from_secs_f32(1.0)); in_1_sec.spawn(DummyComponent); in_1_sec.spawn(DummyComponent); in_1_sec.spawn(DummyComponent); } ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
61 lines
1.8 KiB
Rust
61 lines
1.8 KiB
Rust
//! This example demonstrates how to send commands which will take effect after a period of time.
|
|
//!
|
|
//! We've chosen to demonstrate this effect through the creation of a grid of clickable,
|
|
//! with "ripples" created when you click.
|
|
|
|
use bevy::prelude::*;
|
|
|
|
fn main() {
|
|
App::new()
|
|
.add_plugins(DefaultPlugins)
|
|
.add_systems(Startup, spawn)
|
|
.add_observer(click)
|
|
.run();
|
|
}
|
|
|
|
#[derive(Component)]
|
|
struct BlinkySquare;
|
|
|
|
const SQUARE_SIZE: Vec2 = Vec2::splat(45.0);
|
|
|
|
fn spawn(mut commands: Commands) {
|
|
commands.spawn(Camera2d);
|
|
for x in -5..=5 {
|
|
for y in -5..=5 {
|
|
commands.spawn((
|
|
BlinkySquare,
|
|
Transform::from_xyz(x as f32 * 50.0, y as f32 * 50.0, 0.0),
|
|
Sprite::from_color(Color::BLACK, SQUARE_SIZE),
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
fn click(
|
|
click: On<Pointer<Click>>,
|
|
mut commands: Commands,
|
|
squares: Query<(Entity, &Transform), With<BlinkySquare>>,
|
|
cameras: Query<(&Camera, &GlobalTransform)>,
|
|
) {
|
|
let (camera, camera_transform) = cameras.single().unwrap();
|
|
let mut delayed = commands.delayed();
|
|
for (entity, transform) in squares.iter() {
|
|
// convert the pointer position to world position
|
|
let mouse_world_pos = camera
|
|
.viewport_to_world_2d(camera_transform, click.pointer_location.position)
|
|
.unwrap();
|
|
|
|
// delay the blinkiness by distance to cursor
|
|
let dist = mouse_world_pos.distance(transform.translation.truncate());
|
|
let delay = dist / 1000.0;
|
|
delayed
|
|
.secs(delay)
|
|
.entity(entity)
|
|
.insert(Sprite::from_color(Color::WHITE, SQUARE_SIZE));
|
|
delayed
|
|
.secs(delay + 0.1)
|
|
.entity(entity)
|
|
.insert(Sprite::from_color(Color::BLACK, SQUARE_SIZE));
|
|
}
|
|
}
|