Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Add example to show how to use apply_system_buffers #7793

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,16 @@ description = "Full guide to Bevy's ECS"
category = "ECS (Entity Component System)"
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]]
name = "component_change_detection"
path = "examples/ecs/component_change_detection.rs"
Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ 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
[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
Expand Down
170 changes: 170 additions & 0 deletions examples/ecs/apply_system_buffers.rs
Original file line number Diff line number Diff line change
@@ -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()
};
}
}