#Unparented UI element just floating there

30 messages · Page 1 of 1 (latest)

pliant galleon
#

I have a mystery unparented UI element that is created as an unintended byproduct of another system that otherwise works smoothly. Here's the code:

Eventually this system will put an image of an item in the item slots. For now it just puts the entity id in there.

#[derive(Component)]
struct DisplayItem(Option<Entity>);

fn display_item_system(
    mut commands: Commands,
    query: Query<(Entity, &DisplayItem), Changed<DisplayItem>>,
) {
    for (entity, display_item) in query.iter() {
        commands.entity(entity).clear_children();
        if let Some(item_entity) = display_item.0 {
            println!("{:?} displaying {:?}", entity, item_entity);
            // If there is an item, display it
            commands.entity(entity).with_children(|parent| {
                parent.spawn(TextBundle::from_section(
                    format!("{:?}", item_entity),
                    TextStyle {
                        font: Default::default(),
                        font_size: 20.0,
                        color: Color::rgb(0.9, 0.9, 0.9),
                    },
                ));
            });
        }
    }
}
#

This is the system where I create the entities that have the DisplayItem components:

fn update_inventory_ui_elements(
    slot_query: Query<&InventorySlots, (With<LocalPlayer>, Or<(Changed<InventorySlots>, Added<InventorySlots>)>)>,
    slot_parent_query: Query<Entity, With<InventorySlotParent>>,
    mut commands: Commands,
) {
    if let Ok(inventory_slot_parent_entity) = slot_parent_query.get_single() {
        for slots in slot_query.iter() {
            commands.entity(inventory_slot_parent_entity).clear_children();
            for (i, slot) in slots.0.iter().enumerate() {

                //Spawn new UI elements for each slot
                let slot_entity = commands.spawn((
                    ButtonBundle {
                        style: Style {
                            justify_content: JustifyContent::FlexStart,
                            flex_direction: FlexDirection::Row,
                            margin: UiRect::all(Val::Px(2.0)),
                            ..default()
                        },
                        background_color: BackgroundColor(Color::rgba(0.15, 0.15, 0.15, 0.8)),
                        ..default()
                    },
                    InventorySlotMap(i),
                    DisplayItem(*slot),
                )).id();

                // Add the newly created slot UI element to the parent entity
                commands.entity(inventory_slot_parent_entity).add_child(slot_entity);
            }
        }
    } else {
        eprintln!("InventorySlotParent entity not found");
    }
}
#

Attached is a gif of these systems in action. As you can see there is an unparented display for one of the selected items in the top left. Since it's theoretically the only unparented DisplayItem entity, I tried deleting it with the following system:

#
fn clean_up_system(
    kill_me: Query<Entity, (Without<Parent>, With<DisplayItem>)>,
    mut commands: Commands,
) {
    for entity in kill_me.iter() {
        commands.entity(entity).despawn();
    }
}

This system causes a crash when I click on an item (i.e., when the rogue DisplayItem entity would normally appear). The error is as follows:

thread 'Compute Task Pool (1)' panicked at C:\Users\willi\.cargo\registry\src\index.crates.io-6f17d22bba15001f\taffy-0.3.18\src\node.rs:246:29:
invalid SlotMap key used
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Encountered a panic in system `bevy_ui::layout::ui_layout_system`!
Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
error: process didn't exit successfully: `target\debug\demiurge.exe` (exit code: 101)
pliant galleon
#

This all seems very strange to me. I only use DisplayItem in 2 places, and in both the entity is created with a parent so this should be impossible

light warren
#

You probably could fix that by filtering the entity in the system where you update the display, But that doesn't solve the problem of where the system is created.

pliant galleon
#

Like only update the display if the display entity has a parent?

light warren
#

my guess: the problem is that the entity at fault has a DisplayItem, when you use Changed<DisplayItem>, the system detects another entity that has Changed somehow (the ghost entity)

pliant galleon
#

I'll give it a shot

light warren
#

Btw, other thing that is bugging me is that the problem only happen when you click Removing the entity from a slot. Maybe you should give a look at the system that do that

pliant galleon
#

So strange. I've confirmed this rogue UI element doesn't have a Parent component, but adding Without<Parent> to my display_item_system does nothing.

#

Here's the code that governs adding and removing an item from a slot:

fn try_select_inventory_slot_system(
    mut selected_query: Query<(
        &mut SelectedItem,
        &mut InventorySlots,
    )>,
    mut select_events: EventReader<SelectedInventorySlot>,
) {
    for event in select_events.iter() {
        for (mut selected_item, mut inventory_slots) in selected_query.iter_mut() {
            if selected_item.0.is_some() {
                let selected_item_entity = selected_item.0.unwrap();

                let slot_item_entity = match inventory_slots.0.get_mut(event.0) {
                    Some(slot) => slot.take(),
                    None => None,
                };

                if let Some(slot) = inventory_slots.0.get_mut(event.0) {
                    *slot = Some(selected_item_entity);
                }

                selected_item.0 = slot_item_entity;
            } else {
                if let Some(slot) = inventory_slots.0.get_mut(event.0) {
                    selected_item.0 = slot.take();
                }
            }
        }
    }
}
light warren
ionic ferry
#

I haven't read the convo yet but you should avoid using despawn and instead always use despawn_recursive.
despawn doesn't handle child/parent entities properly.

pliant galleon
ionic ferry
#

The clean_up_system you messaged above thats causing the crash uses despawn

pliant galleon
# light warren Just to be sure, does this system recreate all the slots when try\_select\_inven...

There are 4 systems which govern the inventories behavior.

update_inventory_ui_elements detects when InventorySlots (the component that holds the actual entity data about the inventory) changes. When it changes it uses .clear_children() to despawn all the current buttons and makes new ones in their place.

click_inventory_slot_system detects Interaction::Pressed events on any button with an InventorySlotMap component and fires an event.

try_select_inventory_slot_system detects those events and puts the currently selected item into the clicked slot, and makes the item in the slot the currently selected item (swaps them around basically)

and finally the display_item_system which gets any entity with a changed DisplayItem component and re-displays it.

So... yes. update_inventory_ui_elements will detect the changes made in try_select_inventory_slot_system and will remake all the buttons.

pliant galleon
#

Here's the new system as per your advice.

fn clean_up_system(
    kill_me: Query<Entity, (Without<Parent>, With<DisplayItem>)>,
    mut commands: Commands,
) {
    for entity in kill_me.iter() {
        commands.entity(entity).despawn_recursive();
    }
}

When I click on an item (thus causing the rogue entity to appear) it causes this crash:

thread 'Compute Task Pool (5)' panicked at C:\Users\willi\.cargo\registry\src\index.crates.io-6f17d22bba15001f\taffy-0.3.18\src\node.rs:246:29:
invalid SlotMap key used
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Encountered a panic in system `bevy_ui::layout::ui_layout_system`!
Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
error: process didn't exit successfully: `target\debug\demiurge.exe` (exit code: 101)
ionic ferry
#

Maybe you meant to use .despawn_descendants() instead of .clear_children()?

pliant galleon
#

Thanks so much everyone!

light warren
#

So the problem was in fact the despawn

pliant galleon
#

So does clear children just free the children?

light warren
#

Good to know

pliant galleon
light warren
pliant galleon
#

That makes sense I suppose. Good to know...