-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
Make one-shot example more noob friendly #13201
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice. Feels more approachable this way 👍
Tested it out and works as expected.
If you want to be a perfectionist I guess you'd add a button to tick the server one step, then you can reliably print out if one or both of the systems were triggered in that tick. But then there's probably too much unrelated code. I'm fine with this balance between minimal and easy to understand. With current change you get terminal printouts of any activated systems and the in window text shows the last system ran. I dont want to push since it is in the merge queue but if we add another 25 lines we could have this: //! Demonstrates the use of "one-shot systems", which run once when triggered.
//!
//! These can be useful to help structure your logic in a push-based fashion,
//! reducing the overhead of running extremely rarely run systems
//! and improving schedule flexibility.
//!
//! See the [`World::run_system`](World::run_system) or
//! [`World::run_system_once`](World#method.run_system_once_with)
//! docs for more details.
use bevy::{
ecs::system::{RunSystemOnce, SystemId},
prelude::*,
winit::WinitSettings,
};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
// Only run the app when there is user input. This will significantly reduce CPU/GPU use.
.insert_resource(WinitSettings::desktop_app())
.add_systems(
Startup,
(
setup_ui,
setup_with_commands,
setup_with_world.after(setup_ui), // since we run `system_b` once in world it needs to run after `setup_ui`
),
)
.add_systems(
Update,
((trigger_system, world_run_once), evaluate_callbacks).chain(),
)
.run();
}
#[derive(Component)]
struct Callback(SystemId);
#[derive(Component)]
struct Triggered;
#[derive(Component)]
struct A;
#[derive(Component)]
struct B;
fn setup_with_commands(mut commands: Commands) {
let system_id = commands.register_one_shot_system(system_a);
commands.spawn((Callback(system_id), A));
}
fn setup_with_world(world: &mut World) {
let system_id = world.register_system(system_b);
world.spawn((Callback(system_id), B));
}
/// Tag entities that have callbacks we want to run with the `Triggered` component.
fn trigger_system(
mut commands: Commands,
query_a: Query<Entity, With<A>>,
query_b: Query<Entity, With<B>>,
input: Res<ButtonInput<KeyCode>>,
) {
if input.just_pressed(KeyCode::KeyA) {
let entity = query_a.single();
commands.entity(entity).insert(Triggered);
}
if input.just_pressed(KeyCode::KeyB) {
let entity = query_b.single();
commands.entity(entity).insert(Triggered);
}
}
/// Use world to run system by function name
fn world_run_once(world: &mut World) {
let input = world.resource::<ButtonInput<KeyCode>>();
if input.just_pressed(KeyCode::KeyA) || input.just_pressed(KeyCode::KeyB) {
world.run_system_once(reset_text);
}
}
/// Runs the systems associated with each `Callback` component if the entity also has a `Triggered` component.
///
/// This could be done in an exclusive system rather than using `Commands` if preferred.
fn evaluate_callbacks(query: Query<(Entity, &Callback), With<Triggered>>, mut commands: Commands) {
for (entity, callback) in query.iter() {
commands.run_system(callback.0);
commands.entity(entity).remove::<Triggered>();
}
}
fn system_a(mut query: Query<&mut Text>) {
let mut text = query.single_mut();
text.sections[2].value = String::from("A");
info!("A: One shot system registered with Commands was triggered");
}
fn system_b(mut query: Query<&mut Text>) {
let mut text = query.single_mut();
text.sections[3].value = String::from("B");
info!("B: One shot system registered with World was triggered");
}
fn reset_text(mut query: Query<&mut Text>) {
let mut text = query.single_mut();
text.sections[2].value = String::from("-");
text.sections[3].value = String::from("-");
info!("reset_text was run once from world");
}
fn setup_ui(mut commands: Commands) {
commands.spawn(Camera2dBundle::default());
commands.spawn(
TextBundle::from_sections([
TextSection::new(
"Press A or B to trigger a one-shot system\n",
TextStyle {
font_size: 25.0,
..default()
},
),
TextSection::new(
"Last Triggered: ",
TextStyle {
font_size: 20.0,
..default()
},
),
// DID A run?
TextSection::new(
"-",
TextStyle {
font_size: 20.0,
color: bevy::color::palettes::css::ORANGE.into(),
..default()
},
),
// DID B run?
TextSection::new(
"-",
TextStyle {
font_size: 20.0,
color: bevy::color::palettes::css::ORANGE.into(),
..default()
},
),
])
.with_text_justify(JustifyText::Center)
.with_style(Style {
align_self: AlignSelf::Center,
justify_self: JustifySelf::Center,
..default()
}),
);
}``` |
Objective
If the current example is used as is, the
button_pressed
system will run every update.Update the example so that it is a more ready to use for people
Solution
Rewrote most of it.
Another solution would be to just minimally fix the problems
and
Testing
Ran the example and pressed A / B on the keyboard