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 generic systems example #2636

Closed
wants to merge 10 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
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,10 @@ path = "examples/ecs/event.rs"
name = "fixed_timestep"
path = "examples/ecs/fixed_timestep.rs"

[[example]]
name = "generic_system"
path = "examples/ecs/generic_system.rs"

[[example]]
name = "hierarchy"
path = "examples/ecs/hierarchy.rs"
Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ Example | File | Description
`component_change_detection` | [`ecs/component_change_detection.rs`](./ecs/component_change_detection.rs) | Change detection on components
`event` | [`ecs/event.rs`](./ecs/event.rs) | Illustrates event creation, activation, and reception
`fixed_timestep` | [`ecs/fixed_timestep.rs`](./ecs/fixed_timestep.rs) | Shows how to create systems that run every fixed timestep, rather than every tick
`generic_system` | [`ecs/generic_system.rs`](./ecs/generic_system.rs) | Shows how to create systems that can be reused with different types
`hierarchy` | [`ecs/hierarchy.rs`](./ecs/hierarchy.rs) | Creates a hierarchy of parents and children entities
`iter_combinations` | [`ecs/iter_combinations.rs`](./ecs/iter_combinations.rs) | Shows how to iterate over combinations of query results.
`parallel_query` | [`ecs/parallel_query.rs`](./ecs/parallel_query.rs) | Illustrates parallel queries with `ParallelIterator`
Expand Down
91 changes: 91 additions & 0 deletions examples/ecs/generic_system.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use bevy::{ecs::component::Component, prelude::*};

/// Generic types allow us to reuse logic across many related systems,
/// allowing us to specialize our function's behavior based on which type (or types) are passed in.
///
/// This is commonly useful for working on related components or resources,
/// where we want to have unique types for querying purposes but want them all to work the same way.
/// This is particularly powerful when combined with user-defined traits to add more functionality to these related types.
/// Remember to insert a specialized copy of the system into the schedule for each type that you want to operate on!
///
/// For more advice on working with generic types in Rust, check out <https://doc.rust-lang.org/book/ch10-01-syntax.html>
/// or <https://doc.rust-lang.org/rust-by-example/generics.html>

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
enum AppState {
MainMenu,
InGame,
}

#[derive(Component)]
struct TextToPrint(String);

#[derive(Component)]
struct PrinterTick(bevy::prelude::Timer);

#[derive(Component)]
struct MenuClose;

#[derive(Component)]
struct LevelUnload;

fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_state(AppState::MainMenu)
.add_startup_system(setup_system)
.add_system(print_text_system)
.add_system_set(
SystemSet::on_update(AppState::MainMenu).with_system(transition_to_in_game_system),
)
// add the cleanup systems
.add_system_set(
// Pass in the types your system should operate on using the ::<T> (turbofish) syntax
SystemSet::on_exit(AppState::MainMenu).with_system(cleanup_system::<MenuClose>),
B-Janson marked this conversation as resolved.
Show resolved Hide resolved
)
.add_system_set(
SystemSet::on_exit(AppState::InGame).with_system(cleanup_system::<LevelUnload>),
)
.run();
}

fn setup_system(mut commands: Commands) {
commands
.spawn()
.insert(PrinterTick(bevy::prelude::Timer::from_seconds(1.0, true)))
.insert(TextToPrint(
"I will print until you press space.".to_string(),
))
.insert(MenuClose);

commands
.spawn()
.insert(PrinterTick(bevy::prelude::Timer::from_seconds(1.0, true)))
.insert(TextToPrint("I will always print".to_string()))
.insert(LevelUnload);
}

fn print_text_system(time: Res<Time>, mut query: Query<(&mut PrinterTick, &TextToPrint)>) {
for (mut timer, text) in query.iter_mut() {
if timer.0.tick(time.delta()).just_finished() {
info!("{}", text.0);
}
}
}

fn transition_to_in_game_system(
mut state: ResMut<State<AppState>>,
keyboard_input: Res<Input<KeyCode>>,
) {
if keyboard_input.pressed(KeyCode::Space) {
state.set(AppState::InGame).unwrap();
}
}

// Type arguments on functions come after the function name, but before ordinary arguments.
// Here, the `Component` trait is a trait bound on T, our generic type
fn cleanup_system<T: Component>(mut commands: Commands, query: Query<Entity, With<T>>) {
B-Janson marked this conversation as resolved.
Show resolved Hide resolved
for e in query.iter() {
commands.entity(e).despawn_recursive();
}
}