Несколько кнопок без нескольких систем кнопок?

#rust #bevy

#Ржавчина #стайка

Вопрос:

Я довольно новичок в rust и новичок в bevy. Я понимаю, что это первые дни, и мне очень нравится bevy, но, честно говоря, я нахожу, что примеров и документов немного не хватает.

Я использую в /examples/ui/button.rs качестве отправной точки. И я хочу добавить кучу кнопок вместо одной.

Я ищу способ отличить, какая кнопка была нажата.

Я понимаю, что мог бы добавить систему для каждой кнопки, но это не может быть правильным способом на любом языке.

Итак, я начал добавлять маркеры (как предлагает bevy-cheatsheet).

 commands
    .spawn(ButtonComponents { /* cut for brevity */ })
    .with_children(|parent| {
        parent
            .spawn(TextComponents {  /* cut for brevity */ })
            .with(Marker1);
    });
  

Но как мне тогда проверить, с каким маркером была создана кнопка?

 fn button_system(
    button_materials: Res<ButtonMaterials>,
    mut interaction_query: Query<(
        amp;Button,
        Mutated<Interaction>,
        amp;mut Handle<ColorMaterial>,
        amp;Children,
    )>,
    text_query: Query<amp;mut Text>,
) {
    for (_button, interaction,  mut material, children) in amp;mut interaction_query.iter() {
        let mut text = text_query.get_mut::<Text>(children[0]).unwrap();
        match *interaction {
            Interaction::Clicked => {            

                // This obviously doesn't work, just to illustrate what I'm looking for.
                match text.spawned_with {
                    Marker1 => doSomething(),
                    Marker2 => doBarrelRoll(),
                    _ => unreachable!()
                }    

            }
            Interaction::Hovered => {
                text.value = "Hover".to_string();
                *material = button_materials.hovered.clone();
            }
            Interaction::None => {
                text.value = "Button".to_string();
                *material = button_materials.normal.clone();
            }
        }
    }
}
  

Любые подсказки приветствуются, спасибо!

Редактировать: И теперь я в замешательстве, потому что это действительно работает для кнопки 1 (но сбой button2):

 Interaction::Clicked => {
    let marker = text_query.get::<_>(children[0]).unwrap();
    match *marker {
        Marker1 => println!("marker 1"),
        _ => unreachable!(),
    }
}
  

Но это даже не создает:

     let marker = text_query.get::<_>(children[0]).unwrap();
    match *marker {
        Marker1 => println!("marker 1"),
        Marker2 => println!("marker 2"),
        _ => unreachable!(),
    }
  

Это ошибка:

    |                     expected struct `Marker1`, found struct `Marker2`
   |                     `Marker2` is interpreted as a unit struct, not a new binding
   |                     help: introduce a new binding instead: `other_marker2`
  

Ответ №1:

Хорошо, я нашел ответ, но если у вас есть что-то более элегантное, я был бы рад узнать и сделать это вместо правильного ответа!

 Interaction::Clicked => {
    if let Ok(_) = text_query.get::<Marker1>(children[0]) {
        println!("marker 1")
    }
    if let Ok(_) = text_query.get::<Marker2>(children[0]) {
        println!("marker 2")
    }
    if let Ok(_) = text_query.get::<Marker3>(children[0]) {
        doBarrelRoll()
    }
    ...
}
  

После комментария Даниэля Кулманса я получил что-то вроде этого:

 #[derive(PartialEq)]    // needed for comparison
pub enum Buttons {
    MyFirstButton,
    MySecondButton,
}
struct MyButton {
    target: Buttons,
}
  

И…

 commands
    .spawn(ButtonComponents { /* cut for brevity */ })
    .with_children(|parent| {
        parent
            .spawn(TextComponents {  /* cut for brevity */ })
            .with(MyButton { target: Buttons }); 
    });
  

И…

 Interaction::Clicked => {
    if let Ok(btn) = text_query.get_mut::<MyButton>(children[0]) {
        match btn.target {
            Buttons::MyFirstButton => {
               ...
            },
            Buttons::MySecondButton => {
               ...
            },
            _ => unreachable!(),
        }
    }
}
  

Комментарии:

1. Я понятия не имею, как работает bevy, но я предполагаю, что вы должны использовать не пустую структуру маркера, а структуру, содержащую целое число, которое сообщает вам, какая кнопка была нажата.

2. @danielkullmann Super! я думаю, это привело меня к достойному решению.

Ответ №2:

Хотя я почти на два года опоздал с обсуждением, сегодня я столкнулся с той же проблемой и думаю, что нашел хорошее решение. Я черпал вдохновение из оригинального сообщения @ ippi, где они использовали пустые структуры маркеров. Эти структуры также описаны здесь: https://bevy-cheatbook.github.io/programming/queries.html .

Итак, давайте предположим, что у нас есть две кнопки в пользовательском интерфейсе. Кнопка запуска и кнопка выхода. Сначала мы добавляем структуры маркеров для обеих кнопок:

 #[derive(Component)]
struct StartButtonMarker;

#[derive(Component)]
struct QuitButtonMarker;
  

Во-вторых, мы создаем обе кнопки с их соответствующими структурами маркеров, добавленными к ним в качестве компонентов (с использованием insert ).

 commands.spawn_bundle(ButtonBundle { /* snip */ }).insert(StartButtonMarker);
commands.spawn_bundle(ButtonBundle { /* snip */ }).insert(QuitButtonMarker);
  

Теперь в нашей системе мы можем запрашивать оба маркера как необязательные значения (используя Option<T> тип). Затем мы можем проверить, существует ли маркер для объекта, используя is_some функцию в экземпляре опции:

 fn update(
    mut query: Query<
        (
            amp;Interaction,
            (Option<amp;StartButtonMarker>, Option<amp;QuitButtonMarker>),
        ),
        (Changed<Interaction>, With<Button>),
    >,
) {
    for (interaction, (marker_start, marker_quit)) in amp;mut query {
        if *interaction == Interaction::Clicked {
            if marker_start.is_some() {
                println!("Start button pressed");
            }
            if marker_quit.is_some() {
                println!("Quit button pressed");
            }
        }
    }
}