4 releases
0.2.1 | May 26, 2023 |
---|---|
0.2.0 | May 25, 2023 |
0.1.1 | May 23, 2023 |
0.1.0 | May 23, 2023 |
#1272 in Procedural macros
18KB
162 lines
bevy_ergo_plugin
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.
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 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 {
#[run_if(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>();
}
}
Dependencies
~0.6–1.1MB
~25K SLoC