#How to modify entity on custom trigger

19 messages · Page 1 of 1 (latest)

icy laurel
#

This is probably a super basic thing as I'm very new to this but I'm kinda stumped. So far I only played around a bit with Bevy UI stuff. I saw in examples how you can use Interaction of buttons to affect them. But I added some UI (a node with some content) and want to update that UI on some custom trigger. I can implement the trigger in another system, but then I don't have access to the UI components I added in that system. How can I specify this node to modify it in my trigger system? Any query I can think of could only select all nodes, not this in particular. Do I need to spawn some custom entity containing the node instead of a vanilla node? Do I need to store a reference to the node in a resource?

burnt nacelle
#

i think you are thinking of Observers

#

create an Event, create a system that has the FIRST paramenter as Trigger<Event>, and when creating the entity of the UI node also use .observe(system_name)

#
#[derive(Event)]
struct UiEvent;

fn system_on_ui_event(trigger: Trigger<UiEvent>, ...) {
  ...
}

fn create_ui_node(mut commands: Commands) {
  commands.spawn(Node { ... }).observe(system_on_ui_event);
}

fn trigger_ui_event(mut commands: Commands, ...) {
  if <condition> {
    commands.trigger(UiEvent);
  }
}
spring harbor
#

Remember that you can also always create marker components, add them to your Ui nodes, and query for them that way.

#

If your nodes are unique, storing the Entity id in resource is not a bad idea. But if you can have mulitple of such nodes and need general logic for them, then marker components will be better.

icy laurel
# burnt nacelle ```rust #[derive(Event)] struct UiEvent; fn system_on_ui_event(trigger: Trigger...

For some reason my observer system doesn't get called

#[derive(Eq, PartialEq)]
enum Sidebar {
    Closed,
    Open,
}

#[derive(Event)]
struct SidebarEvent(Sidebar);

fn build_gui(mut commands: Commands) {
    let mut menu = commands.spawn(NodeBundle {...});
    menu.observe(sidebar_observer);
}

fn sidebar_observer(
    trigger: Trigger<SidebarEvent>,
    mut style_query: Query<&mut Style>,
) {
    if let Ok(mut style) = style_query.get_mut(trigger.entity()) {
        if trigger.event().0 == Sidebar::Open {
            println!("Sidebar opening!");
            style.display = Display::Block;
        } else {
            println!("Sidebar closing!");
            style.display = Display::None;
        }
    } else {
        println!("Can't find sidebar :( ({:?})", trigger.entity())
    }
}

fn cursor_pos_system(
    mut mouse_motion_events: EventReader<CursorMoved>,
    mut commands: Commands
) {
    for ev in mouse_motion_events.read() {
        if ev.position.y < 50.0 {
            println!("Sending Open event");
            commands.trigger(SidebarEvent(Sidebar::Open))
        }
        if ev.position.y > 350.0 {
            println!("Sending Closed event");
            commands.trigger(SidebarEvent(Sidebar::Closed))
        }
    }
}

fn build(&self, app: &mut App) {
    app.add_systems(Startup, build_gui);
    app.add_systems(Update, cursor_pos_system);
}

I'm getting the output from cursor_pos_system but none from sidebar_observer

icy laurel
# spring harbor If your nodes are unique, storing the Entity id in resource is not a bad idea. B...

I did it with a resource now. Figured a marker component wouldn't make much sense as all the logic makes assumptions about the position and size of the sidebar.

#[derive(Default, Resource)]
struct SidebarRes {
    entity: Option<Entity>,
    open: bool,
}

fn update_sidebar(
    mut sidebar: ResMut<SidebarRes>,
    mut style_query: Query<&mut Style>,
    mut mouse_motion_events: EventReader<CursorMoved>,
) {
    for ev in mouse_motion_events.read() {
        let margin: f32 = 20.0;
        if ev.position.y < margin && !sidebar.open {
            if let Ok(mut style) = style_query.get_mut(sidebar.entity.unwrap()) {
                style.display = Display::Block;
                sidebar.open = true;
            }
        }
        if ev.position.y > SIDEBAR_HEIGHT + margin && sidebar.open {
            if let Ok(mut style) = style_query.get_mut(sidebar.entity.unwrap()) {
                style.display = Display::None;
                sidebar.open = false;
            }
        }
    }
}

Next up, I'll try to figure out if I can animate the transition...

burnt nacelle
#

I haven't used an observer on entities yet, so I didn't know if you had to pass the entity or not

#

Then you need to use .trigger_with_targets and pass the menu's Entity

#

But looking at your code I think you can do it by using a button node and checking it the button is hovered

#

And translate the menu if the button is hovered or not

burnt nacelle
#

Not translate, change the display

spring harbor
#

Importnat note on the Resource approach: When you do query like Query<&mut Style> you are blocking any other system using Style components form executing in parallel(since you have option to mutable borrow any entity with that component). Adding marker components allow Bevy to figure out disjoint sets and run them in parallel.
It doesn't matter now for you, but it's good to have that in mind for times when you will work on more intensive parts of your game.

icy laurel
#

I think I'll rewrite the same sidebar I did here in bevy_egui to test it

spring harbor
#

I wouldn't say that. You just need to be aware how it works and not everything is yet documented. But exploring other options is always a good idea.

icy laurel
#

I'm referring to

I wanted to do it differently but discovered it would be very hard with current UI in Bevy so I backtracked to this simpler version