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

Make one-shot example more noob friendly #13201

Merged
merged 1 commit into from
May 3, 2024

Conversation

rmsthebest
Copy link
Contributor

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

 .add_systems(Startup, (count_entities, setup).chain()) 

and

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>();
    }
}

Testing

  • Did you test these changes? If so, how?
    Ran the example and pressed A / B on the keyboard

Copy link
Contributor

@jgayfer jgayfer left a 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.

@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events C-Examples An addition or correction to our examples labels May 3, 2024
@alice-i-cecile alice-i-cecile added this pull request to the merge queue May 3, 2024
@alice-i-cecile alice-i-cecile added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label May 3, 2024
@rmsthebest
Copy link
Contributor Author

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()
        }),
    );
}```

Merged via the queue into bevyengine:main with commit 96a6eee May 3, 2024
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events C-Examples An addition or correction to our examples S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants