Crate bevy_ergo_plugin

source ·
Expand description

Macros to make building bevy plugins more ergonomic (in my opinion).

Bevy’s API puts adding a system separate from its implementation. Separatng the system’s run conditions and other system parameters from its definition adds an extra layer of indirection, harming readability and adding boilerplate (.add_system)
This crate’s purpose is to replace that API with a more ergonomic one, using attribute macros as markers for system parameters.
Not only does this allow for more readable system run conditions, but it also gives us fallible systems with logging much more cleanly, while still using bevy’s built-ins.

Putting the bevy_plugin attribute on the impl block of a struct will turn that struct into a Bevy Plugin, which registers its associated functions as systems.
If you want to add extra functionality to your plugin’s Plugin::build (like adding an asset or registering a component), the contents of any associated function named build in a bevy_plugin attributed impl block will be inserted into the generated Plugin::build implementation.

The other macros are the aforementioned parameter markers to be put on your system definitions.
For any params not included by a specific marker, you can use the sysparam marker to define custom behavior.

Multiple parameter markers on a system do stack onto a single add_system call, so you can add multiple run conditions and have it work as expected.

This crate is basically just doing code generation, so it doesn’t depend on bevy itself. It should work with any bevy version that has the functions you’re generating.

This crate has not been thoroughly tested, so there will probably be weird bugs I don’t know about.

Example

adapted from https://github.com/bevyengine/bevy/blob/latest/examples/ecs/run_conditions.rs

use bevy::prelude::*;
use bevy_ergo_plugin::*;

fn main() {
    println!();
    println!("For the first 2 seconds you will not be able to increment the counter");
    println!("Once that time has passed you can press space, enter, left mouse, right mouse or touch the screen to increment the counter");
    println!();

    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugin(Game)
        .run();
}
#[derive(Resource, Default)]
pub struct InputCounter(usize);

pub struct Game;
#[bevy_plugin]
impl Game {
    #[resource_exists(InputCounter)]
    #[run_if(Game::has_user_input)]
    pub fn increment_input_counter(mut counter: ResMut<InputCounter>) {
        counter.0 += 1;
    }

    #[run_if(resource_exists::<InputCounter>().and_then(
        |counter: Res<InputCounter>| counter.is_changed() && !counter.is_added()
    ))]
    pub fn print_input_counter(counter: Res<InputCounter>) {
        println!("Input counter: {}", counter.0);
    }

    #[run_if(Game::time_passed(2.0))]
    #[run_if(not(Game::time_passed(2.5)))]
    pub fn print_time_message() {
        println!(
            "It has been more than 2 seconds since the program started and less than 2.5 seconds"
        );
    }

    #[do_not_add]
    pub fn has_user_input(
        keyboard_input: Res<Input<KeyCode>>,
        mouse_button_input: Res<Input<MouseButton>>,
        touch_input: Res<Touches>,
    ) -> bool {
        keyboard_input.just_pressed(KeyCode::Space)
            || keyboard_input.just_pressed(KeyCode::Return)
            || mouse_button_input.just_pressed(MouseButton::Left)
            || mouse_button_input.just_pressed(MouseButton::Right)
            || touch_input.any_just_pressed()
    }

    #[do_not_add]
    pub fn time_passed(t: f32) -> impl FnMut(Local<f32>, Res<Time>) -> bool {
        move |mut timer: Local<f32>, time: Res<Time>| {
            *timer += time.delta_seconds();
            *timer >= t
        }
    }

    pub fn build(&self, app: &mut App) {
        app.init_resource::<InputCounter>();
    }
}

Attribute Macros

  • system parameter marker attribute that generates
    .add_system( system_name.after(arg) )
  • system parameter marker attribute that generates
    .add_system( system_name.run_if(any_with_component::<arg>()) )
  • system parameter marker attribute that generates
    .add_system( system_name.before(arg) )
  • Put this attribute on an impl block of a struct to turn the struct into a plugin where the functions in the impl block are systems. Use the marker attributes on your systems to add parameters. A function named build can be used to add custom functionality to the Plugin::build in the generated Plugin implementation.
  • system parameter marker attribute that generates
    .add_system( system_name.pipe(dbg) )
  • a marker attribute to exclude a function from being added
  • system parameter marker attribute that generates
    .add_system( system_name.pipe(error) )
  • system parameter marker attribute that generates
    .add_system( system_name.pipe(ignore) )
  • system parameter marker attribute that generates
    .add_system( system_name.in_base_set(arg) )
  • system parameter marker attribute that generates
    .add_system( system_name.in_schedule(arg) )
  • system parameter marker attribute that generates
    .add_system( system_name.in_set(arg) )
  • system parameter marker attribute that generates
    .add_system( system_name.run_if(in_state(arg)) )
  • system parameter marker attribute that generates
    .add_system( system_name.pipe(info) )
  • system parameter marker attribute that generates
    .add_system( system_name.run_if(not(arg)) )
  • system parameter marker attribute that generates
    .add_system( system_name.in_schedule(OnEnter(arg)) )
  • system parameter marker attribute that generates
    .add_system( system_name.run_if(on_event::<arg>()) )
  • system parameter marker attribute that generates
    .add_system( system_name.in_schedule(OnExit(arg)) )
  • system parameter marker attribute that generates
    .add_system( system_name.in_set(OnUpdate(arg)) )
  • system parameter marker attribute that generates
    .add_system( system_name.pipe(arg) )
  • system parameter marker attribute that generates
    .add_system( system_name.run_if(resource_added::<arg>()) )
  • system parameter marker attribute that generates
    .add_system( system_name.run_if(resource_changed::<arg>()) )
  • system parameter marker attribute that generates
    .add_system( system_name.run_if(resource_changed_or_removed::<arg>()) )
  • system parameter marker attribute that generates
    .add_system( system_name.run_if(resource_equals(arg)) )
  • system parameter marker attribute that generates
    .add_system( system_name.run_if(resource_exists::<arg>()) )
  • system parameter marker attribute that generates
    .add_system( system_name.run_if(resource_exists_and_changed::<arg>()) )
  • system parameter marker attribute that generates
    .add_system( system_name.run_if(resource_exists_and_equals(arg)) )
  • system parameter marker attribute that generates
    .add_system( system_name.run_if(resource_removed::<arg>()) )
  • system parameter marker attribute that generates
    .add_system( system_name.run_if(arg) )
  • system parameter marker attribute that generates
    .add_system( system_name.run_if(run_once()) )
  • system parameter marker attribute that generates
    .add_system( system_name.on_startup() )
  • system parameter marker attribute that generates
    .add_system( system_name.run_if(state_changed::<arg>()) )
  • system parameter marker attribute that generates
    .add_system( system_name.run_if(state_exists::<arg>()) )
  • system parameter marker attribute that generates
    .add_system( system_name.run_if(state_exists_and_equals(arg)) )
  • system parameter marker attribute that generates
    .add_system( system_name.arg )
  • system parameter marker attribute that generates
    .add_system( system_name.pipe(unwrap) )
  • system parameter marker attribute that generates
    .add_system( system_name.pipe(warn) )