7 releases (breaking)

0.7.0 Nov 15, 2022
0.6.0 Aug 1, 2022
0.5.0 Jun 22, 2022
0.4.0 Jun 21, 2022
0.1.0 May 30, 2022

#2203 in Game dev

MIT/Apache

18KB
136 lines

Conditional Commands

This crate implements three extension traits that allow for conditional component, bundle and child insertion without the need for an intermediate EntityCommands or EntityMut binding.

  • ConditionalInsertBundleExt for EntityCommands and EntityMut
    Methods:
    • insert_if
    • insert_if_else
    • insert_some
    • insert_some_or
  • ConditionalChildBuilderExt for EntityCommands
    Methods:
    • with_children_if
  • ConditionalWorldChildBuilderExt for EntityMut
    Methods:
    • with_children_if

Supports Bevy 0.9

Usage

To add to a project either use:

cargo add conditional_commands

or manually add to your Cargo.toml:

[dependencies]
conditional_commands = "0.9.0"

A Motivating But Contrived ECS Fizz-Buzz Example

use bevy::prelude::*;
use conditional_commands::*;

#[derive(Component)]
struct FizzBuzzer;

#[derive(Component)]
struct Number(usize);

#[derive(Component)]
struct Fizz;

#[derive(Component)]
struct Buzz;

fn fizz_buzz<const N: usize>(
    mut commands: Commands
) {
    for n in 1 ..= N {
        let mut entity_commands = commands.spawn(FizzBuzzer);
        match (n % 3, n % 5) {
            (0, 0) => { entity_commands.insert((Fizz, Buzz)); },
            (0, _) => { entity_commands.insert(Fizz); },
            (_, 0) => { entity_commands.insert(Buzz); },
            _ => { entity_commands.insert(Number(n)); }
        }
    }
}

With Conditional Commands the intermediate EntityCommands binding in no longer required.

fn fizz_buzz<const N: usize>(
    mut commands: Commands
) {
    for n in 1 ..= N {
        commands
        .spawn(FizzBuzzer)
        .insert_if(0 < n % 3 && 0 < n % 5, || Number(n))
        .insert_if(n % 3 == 0, || Fizz)
        .insert_if(n % 5 == 0, || Buzz);
    }
}

ConditionalInsertBundleExt is also implemented for EntityMut:

#[derive(Component)]
struct Even;

#[derive(Component)]
struct Odd;

fn exclusive_system(world: &mut World) {
    for n in 0..10 {
        world.spawn_empty().insert_if_else(n % 2 == 0, || Even, || Odd);
    }
}    

Bundles passed to the _else/_or methods don't need to be the same type, as seen in the above example with the Even and Odd components.

Use insert_some to insert the inner value of an optional bundle, if present.

commands.spawn(MyBundle)
    .insert_some(Some(OtherBundle::default()))
    .insert_some_or(None::<MyThing>, AlternativeThing::default);

Examples

cargo run --example exclusive
cargo run --example fizz_buzz
cargo run --example insert_if
cargo run --example insert_if_2
cargo run --example with_children_if

Notes

  • I haven't done any benchmarking. For performance critical systems it should be better to spawn entire bundles at once.

  • Earlier versions of this crate have both eager and lazy versions of each insertion method. I'm not sure the eager versions had any advantages (beyond no ||), so they are gone.

  • I wasn't able to quite finesse the lifetimes to get a single generic child builder trait for both EntityCommands and EntityMut.

Dependencies

~18–34MB
~565K SLoC