#bevy-ecs #bevy #bevy-macro #entity #macro #picking #syntax #mevy

macro mevy_ecs

bevy_ecs macros, simplifying child spawning and picking!

4 releases

Uses new Rust 2024

new 0.2.1 Apr 12, 2025
0.2.0 Apr 10, 2025
0.1.2 Apr 7, 2025
0.1.1 Mar 9, 2025
0.1.0 Jan 9, 2025

#2705 in Game dev

Download history 118/week @ 2025-01-04 43/week @ 2025-01-11 3/week @ 2025-01-18 5/week @ 2025-02-01 3/week @ 2025-02-08 7/week @ 2025-02-22 6/week @ 2025-03-01 121/week @ 2025-03-08 22/week @ 2025-03-15 6/week @ 2025-03-22 2/week @ 2025-03-29 236/week @ 2025-04-05

287 downloads per month
Used in mevy

MIT/Apache

35KB
542 lines

These are ONLY proc-macros (no additional traits, structs, fns etc.). This crate is part of mevy (tl;dr: more neat macros) so take a look! 🦆

Setup

Multiple bevy versions are supported and managed by features:

# bevy 0.16.0-rc.4
mevy_ecs = {version="0.2",features=["0.16-rc"]}

# bevy 0.15
mevy_ecs = {version="0.2",features=["0.15"]}

Rough Overview

entity!{
    <..>                  // World/Entity Selection
    Bundle::new(..);      // Insert Bundle to Selected
    .observe(..);         // Use a method
    {..}                  // Free code block w/ 'this: EntityCommands'
    > Pointer<Click>{..}  // Quick Observe w/ 'this: EntityCommands'
    >> Pointer<Click>{..} // Quick Observe w/ 'world: World', 'entity: Entity'
    [                     // Spawn a Child
        Bundle::new(..);  // Insert Bundle to Child
        .observe(..);     // Use method on Childs EnitityCommands/EntityWorldMut
        {ancestors[0]}    // ancestors: Vec<Entity>
                          // first = parent, last = 'selected root'
    ]
    [named_child][        // Spawn a Child with a name
        // ..
    ]
}

Alternative Syntax

entity!{
    <..>

    Bundle!;       // = Bundle::default();
    Bundle{a:3,!}; // = Bundle{a:3,..default()};
    Bundle: 3;     // = Bundle::new(3);
    bundle_fn: 3;  // = bundle_fn(3);
    macro!: 3, 4;  // = macro!{3,4};
    .method: 5;    // = .macro(5);
    
    // any plain Hex-Code = [Color]
    BorderColor(#ff0000);

    // css like [Val]s
    Node{
        left:   10px;
        top:    5%;
        width:  3vw;
        height: 6vmax;
    !};

}

World/Entity Selection

The first entry of the macro determines which world access is used and which entity is aimed.

entity!{

    <cmd>       // SPAWN an entity using this [Commands]
                // (no entry) assumes a 'world: Commands'

    <cmd|enty>  // MODIFY an entity: pass a Commands | Entity
    <|enty>     // assumes a 'world: Commands'
    <cmd|>      // assumes a 'me: Entity'
    <|>         // assumes both

    <+world>    // SPAWN using this [World]
    <+world|..> // MODIFY using this [World]
    <+> <+|..>  // assumes a 'world: World'

    <-world>    // SPAWN using this [DeferredWorld]
    <-world|..> // MODIFY using this [DeferredWorld]
    <-> <-|..>  // assumes a 'world: DeferredWorld'

    <*this>     // MODIFY using this [EntityCommands]
    <*>         // assumes a 'world: EntityCommands'
    <>          // assumes a 'this: EntityCommands'

    <^this>     // MODIFY the parent of a [ChildBuilder]
    <^>         // assumes a 'world: ChildBuilder'

    <+*this>    // MODIFY using this [EntityWorldMut]
    <+*>        // assumes a 'world: EntityWorldMut'

    <|#Comp>         // target EVERY entity with this component
    <|#Comp.get()>   // ..or an [Option<Entity>] of the component
    <|#*Comp.all()>  // ..or any iterator over [Entity]s
    
    <|!#Comp>        // target THE ONLY (.single()) entity, enables 'leaking'
    <|!#Comp.0>      // ..or an [Entity] on the component

    <|@Comp.get()>   // target an [Option<Entity>] on a resource 
    <|@*Comp.all()>  // ..or any iterator over [Entity]s
    
    <|!@Comp.0>      // target an [Entity] on a resource, enables 'leaking'

}

Redirection

After the initial selection of entities, you can redirect it to entities of a component. This expects: <impl Component>.<path to an impl Iterator<Item=Entity>> or <impl Component>.<path to a Some<Entity>! (mind the '!').

entity!{
    <world|#Marker>             // select: every Entity with [Marker]
    <Children.get(0).cloned()!> // > select: first child, if available
    <Children.iter()>           // >> select all children
    Visibility::Hidden;         // hide all of them
    .despawn();                 // despawn all of them
}

Leaking / Returning

If the selector can 'leak' entities, you can use one of those symbold a the END of the macro:

  • > 'leak': every spawned entity is available in this scope
  • < 'return': returns the root entity
let enty = entity!{
    Bundle::new(5);
<};

entity!{
    Bundle::new(5);
    []
    [named][]
>}    
me;   // the spawned entity
e1;   // the unnamed child entity
named // the named child entity

entity!{
    <|!#Comp>
    [named][]
>}
named // resource/component selectors only leak children

Quick Observe

A simpler way to use triggers on self, basically means goated event control:

spawn!{
    // Using '>'
    > Pointer<Click> {
        // Provided variables:
        // 'this' = EnitityCommands
        // 'event' = e.g. &Pointer<Click>
        this.despawn();
    }
    // Using '>>'
    >> Pointer<Click> {
        // Provided variables:
        // 'world' = mut World
        // 'entity' = Entity
        // 'event' = e.g. &Pointer<Click>
    }
}
fn startup(mut world: Commands){
    spawn!{Camera2d::default()}
}

Child Names

The 'Child Names' are variables containing the child entity.

  • Inside the macro they can be used anywhere, even 'before' you wrote them
  • If none is provided - one will be generated: e{number}: e0, e1, ...
spawn!{
    Component{ entity: named_child };
    [named_child][
        // components & methods
    ]
}

This is 'token based', which means it preserves LSP greatness.

spawn!{
    // typing . will fire LSPs autocomplete
    .obs // would suggest 'observer'
}

Easy way to address ancestors through a provided array, created within a macro call:

  • first entry: the direct parent
  • last entry: root entity of the current macro call
spawn!{
    [[[[[[
        SpecificEntity(ancestors[3]);
    ]]]]]]
}

Example

Combining the things you could write thing like this:

fn startup(mut world: Commands){
    entity!{
        Name: "Root";
        Node{ padding:10px, !};
        BackgroundColor(#ff0000);
        Marker;
        [nice_text][
            Name: "Some Name";
            Text: "Hello World";
            > Pointer<Click> {this.despawn();};
        ]
    }
}

And modify it by a marker:

fn update(mut world:Commands){
    entity!{
        <|#Marker>
        BackgroundColor(#00ff00);
    }
}

Synergies with mevy

Using mevy_ui macro ui!{}, it's a bit like html/css:

spawn!{
    ui!((
        size: 5px;
        box_shadow: 1px 2px 3px 4px #ff0000;
        background: cyan;
    ));
    [inner_box][ui!((
        size: 80%;
        background: green;
    ))]
}

Dependencies

~2.5MB
~47K SLoC