#bevy #macro #gamedev #impl-block #game

macro bevy_ergo_plugin

Macros to make building bevy plugins more ergonomic

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

#404 in Procedural macros

Download history 6/week @ 2024-02-19 3/week @ 2024-02-26 99/week @ 2024-04-01

99 downloads per month

MIT license

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.7–1.2MB
~27K SLoC