mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Add example to show how to use apply_system_buffers
(#7793)
# Objective This PR adds an example that shows how to use `apply_system_buffers` and how to order it with respect to the relevant systems. It also shows how not ordering the systems can lead to unexpected behaviours. ## Solution Add the example.
This commit is contained in:
parent
8cbef73276
commit
998e983bac
3 changed files with 181 additions and 0 deletions
10
Cargo.toml
10
Cargo.toml
|
@ -882,6 +882,16 @@ description = "Full guide to Bevy's ECS"
|
||||||
category = "ECS (Entity Component System)"
|
category = "ECS (Entity Component System)"
|
||||||
wasm = false
|
wasm = false
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "apply_system_buffers"
|
||||||
|
path = "examples/ecs/apply_system_buffers.rs"
|
||||||
|
|
||||||
|
[package.metadata.example.apply_system_buffers]
|
||||||
|
name = "Apply System Buffers"
|
||||||
|
description = "Show how to use `apply_system_buffers` system"
|
||||||
|
category = "ECS (Entity Component System)"
|
||||||
|
wasm = false
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "component_change_detection"
|
name = "component_change_detection"
|
||||||
path = "examples/ecs/component_change_detection.rs"
|
path = "examples/ecs/component_change_detection.rs"
|
||||||
|
|
|
@ -197,6 +197,7 @@ Example | Description
|
||||||
|
|
||||||
Example | Description
|
Example | Description
|
||||||
--- | ---
|
--- | ---
|
||||||
|
[Apply System Buffers](../examples/ecs/apply_system_buffers.rs) | Show how to use `apply_system_buffers` system
|
||||||
[Component Change Detection](../examples/ecs/component_change_detection.rs) | Change detection on components
|
[Component Change Detection](../examples/ecs/component_change_detection.rs) | Change detection on components
|
||||||
[Custom Query Parameters](../examples/ecs/custom_query_param.rs) | Groups commonly used compound queries and query filters into a single type
|
[Custom Query Parameters](../examples/ecs/custom_query_param.rs) | Groups commonly used compound queries and query filters into a single type
|
||||||
[ECS Guide](../examples/ecs/ecs_guide.rs) | Full guide to Bevy's ECS
|
[ECS Guide](../examples/ecs/ecs_guide.rs) | Full guide to Bevy's ECS
|
||||||
|
|
170
examples/ecs/apply_system_buffers.rs
Normal file
170
examples/ecs/apply_system_buffers.rs
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
//! This example illustrates how to use the `apply_system_buffers` system
|
||||||
|
//! to flush commands added by systems that have already run,
|
||||||
|
//! but have not had their buffers applied yet.
|
||||||
|
//!
|
||||||
|
//! This is useful when you don't want to wait until the next flush set
|
||||||
|
//! automatically added by Bevy (usually `CoreSet::UpdateFlush`, for systems
|
||||||
|
//! added to `CoreSet::Update`) but want to flush commands immediately.
|
||||||
|
//!
|
||||||
|
//! It is important that systems are ordered correctly with respect to
|
||||||
|
//! `apply_system_buffers`, to avoid surprising non-deterministic system execution order.
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
App::new()
|
||||||
|
.add_plugins(DefaultPlugins)
|
||||||
|
.init_resource::<Timers>()
|
||||||
|
.add_startup_system(setup)
|
||||||
|
.add_system(despawn_old_and_spawn_new_fruits.before(CustomFlush))
|
||||||
|
.add_system(apply_system_buffers.in_set(CustomFlush))
|
||||||
|
.add_system(count_apple.after(CustomFlush))
|
||||||
|
.add_system(count_orange)
|
||||||
|
.add_system(bevy::window::close_on_esc)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
struct Timers {
|
||||||
|
repeating: Timer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Timers {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
repeating: Timer::from_seconds(0.5, TimerMode::Repeating),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
|
||||||
|
struct CustomFlush;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct Apple;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct Orange;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct AppleCount;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct OrangeCount;
|
||||||
|
|
||||||
|
// Setup the counters in the UI.
|
||||||
|
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
|
commands.spawn(Camera2dBundle::default());
|
||||||
|
|
||||||
|
commands
|
||||||
|
.spawn(NodeBundle {
|
||||||
|
style: Style {
|
||||||
|
size: Size::new(Val::Percent(100.0), Val::Percent(100.0)),
|
||||||
|
align_items: AlignItems::Center,
|
||||||
|
justify_content: JustifyContent::Center,
|
||||||
|
flex_direction: FlexDirection::Column,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
..default()
|
||||||
|
})
|
||||||
|
.with_children(|parent| {
|
||||||
|
parent.spawn((
|
||||||
|
TextBundle::from_section(
|
||||||
|
"Apple: nothing counted yet".to_string(),
|
||||||
|
TextStyle {
|
||||||
|
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||||
|
font_size: 80.0,
|
||||||
|
color: Color::ORANGE,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
AppleCount,
|
||||||
|
));
|
||||||
|
parent.spawn((
|
||||||
|
TextBundle::from_section(
|
||||||
|
"Orange: nothing counted yet".to_string(),
|
||||||
|
TextStyle {
|
||||||
|
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||||
|
font_size: 80.0,
|
||||||
|
color: Color::ORANGE,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
OrangeCount,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Every tick, before the CustomFlush we added, we despawn any Apple and Orange
|
||||||
|
// we have previously spawned, if any. Then we tick the timer, and if the timer
|
||||||
|
// has finished during this tick, we spawn a new Apple and a new Orange.
|
||||||
|
//
|
||||||
|
// The commands that we have added here will normally be flushed by Bevy
|
||||||
|
// as part of the `CoreSet::UpdateFlush` set, but because we have ordered
|
||||||
|
// this system to run before `apply_system_buffer.in_set(CustomFlush)`,
|
||||||
|
// these commands added here will be flushed during our custom flush.
|
||||||
|
fn despawn_old_and_spawn_new_fruits(
|
||||||
|
mut commands: Commands,
|
||||||
|
time: Res<Time>,
|
||||||
|
mut timers: ResMut<Timers>,
|
||||||
|
apple: Query<Entity, With<Apple>>,
|
||||||
|
orange: Query<Entity, With<Orange>>,
|
||||||
|
) {
|
||||||
|
if let Ok(apple_entity) = apple.get_single() {
|
||||||
|
commands.entity(apple_entity).despawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(orange_entity) = orange.get_single() {
|
||||||
|
commands.entity(orange_entity).despawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
timers.repeating.tick(time.delta());
|
||||||
|
|
||||||
|
if timers.repeating.just_finished() {
|
||||||
|
commands.spawn(Apple);
|
||||||
|
commands.spawn(Orange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the timer has finished during this tick, we see if there is an entity
|
||||||
|
// with an Apple component or not, and update the UI accordingly.
|
||||||
|
//
|
||||||
|
// Since this system is ordered `.after(CustomFlush)` it will be guaranteed
|
||||||
|
// to run after our CustomFlush set, so the Apple will always be counted.
|
||||||
|
//
|
||||||
|
// We will see the AppleCount go from "Apple: nothing counted yet" to "Apple: counted"
|
||||||
|
fn count_apple(
|
||||||
|
timers: Res<Timers>,
|
||||||
|
apple: Query<&Apple>,
|
||||||
|
mut apple_count: Query<&mut Text, With<AppleCount>>,
|
||||||
|
) {
|
||||||
|
if timers.repeating.just_finished() {
|
||||||
|
let mut apples_text = apple_count.single_mut();
|
||||||
|
apples_text.sections[0].value = if apple.is_empty() {
|
||||||
|
"Apple: not counted".to_string()
|
||||||
|
} else {
|
||||||
|
"Apple: counted".to_string()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the timer has finished during this tick, we see if there is an entity
|
||||||
|
// with an Orange component or not, and update the UI accordingly.
|
||||||
|
//
|
||||||
|
// Since this system is not ordered `.after(CustomFlush)`, it may or may not run
|
||||||
|
// before the custom flush, therefore you will see the UI either show "Orange: counted"
|
||||||
|
// or "Orange: not counted" or alternate between the two.
|
||||||
|
//
|
||||||
|
// Try to re-run the example multiple times as well.
|
||||||
|
fn count_orange(
|
||||||
|
timers: Res<Timers>,
|
||||||
|
orange: Query<&Orange>,
|
||||||
|
mut orange_count: Query<&mut Text, With<OrangeCount>>,
|
||||||
|
) {
|
||||||
|
if timers.repeating.just_finished() {
|
||||||
|
let mut oranges_text = orange_count.single_mut();
|
||||||
|
oranges_text.sections[0].value = if orange.is_empty() {
|
||||||
|
"Orange: not counted".to_string()
|
||||||
|
} else {
|
||||||
|
"Orange: counted".to_string()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue