如何在 Bevy 中检测按钮点击和普通点击

huangapple go评论54阅读模式
英文:

How to detect a button click and a normal click in bevy

问题

抱歉,由于您的请求,我将只返回翻译好的代码部分:

fn tower_button_interaction(
  mut commands: Commands,
  windows: Res<Windows>,
  mouse: Res<Input<MouseButton>>,
  assets: Res<GameAssets>,
  interaction: Query<(&Interaction, &TowerType), Changed<Interaction>>
) {
  let window = windows.get_primary().unwrap();
  for (interaction, tower_type) in &interaction {
    match interaction {
      Interaction::Clicked => {
        info!("Spawning: {tower_type} wizard");

        // Upon clicking the mouse, spawn the selected tower on the map
        if mouse.just_pressed(MouseButton::Left) {
          if let Some(position) = window.cursor_position() {
            spawn_tower(&mut commands, *tower_type, &assets, position.extend(0.));
          }
        }
      }
      Interaction::Hovered => {}
      Interaction::None => {}
    }
  }
}

希望这对您有所帮助。如果您需要进一步的解释或帮助,请随时提出。

英文:

I'm making a tower defense game in bevy and I'm having problems with placing a tower. So basically I want to make it so that when you click one of the buttons (each one spawns a different tower) an asset/sprite of the tower should follow your mouse and when the mouse is clicked AGAIN it should spawn the tower.

Currently my program registers the button click, but it goes into 2 ifs so when the button is clicked it automatically spawns a tower somewhere (not even below the button) without waiting for the user to click again. So the Interaction::Clicked only checks if the button was clicked, but doesn't grab the mouse click event, it only reads it so therefore if the mouse is clicked for a longer period of time (human click) the code will go into the second if and spawn the tower (I don't know why it spawns it where it is on the picture below). How can I fix this? Picture:

Towers spawning in some random place. First button spawns them at around (200, 0, 0)

Code:

fn tower_button_interaction(
  mut commands: Commands,
  windows: Res&lt;Windows&gt;,
  mouse: Res&lt;Input&lt;MouseButton&gt;&gt;,
  assets: Res&lt;GameAssets&gt;,
  interaction: Query&lt;(&amp;Interaction, &amp;TowerType), Changed&lt;Interaction&gt;&gt;
) {
  let window = windows.get_primary().unwrap();
  for (interaction, tower_type) in &amp;interaction {
    match interaction {
      Interaction::Clicked =&gt; {
        info!(&quot;Spawning: {tower_type} wizard&quot;);
    
        // Upon clicking the mouse, spawn the selected tower on the map
        if mouse.just_pressed(MouseButton::Left) {
          if let Some(position) = window.cursor_position() {
            spawn_tower(&amp;mut commands, *tower_type, &amp;assets, position.extend(0.));
          }
        }
      }
      Interaction::Hovered =&gt; {}
      Interaction::None =&gt; {}
    }
  }
}

I also tried changing the if mouse.just_pressed(MouseButton::Left) to if matches!(interaction, Interaction::Clicked), but the same thing happened.

答案1

得分: 0

这是指 Bevy 文档中的一个部分,涵盖得相当详细。 https://docs.rs/bevy/latest/bevy/input/struct.Input.html#multiple-systems

如果有多个系统在检查 Input::just_pressed 或 Input::just_released,但只有一个应该做出反应,例如在触发状态变化的情况下,您应该考虑清除输入状态,方法之一是:

使用 Input::clear_just_pressed 或 Input::clear_just_released 而不是。
在状态更改后立即调用 Input::clear 或 Input::reset。

如果您不希望其他系统接收该输入事件,您应该清除输入。

对于您的示例:

if mouse.just_pressed(MouseButton::Left) {
  if let Some(position) = window.cursor_position() {
    spawn_tower(&mut commands, *tower_type, &assets, position.extend(0.));
    mouse.clear_just_pressed(MouseButton::Left); // <- 新增的这一行
  }
}

您还需要确保此系统在其他系统之前运行,以便在其他系统检查输入之前阻止输入。有关有序系统的完整指南,请参阅Bevy Cheatbook: 显式系统排序

它将类似于以下方式:

app
    .add_system(tower_button_interaction)
    .add_system(tower_placement_system.after(tower_button_interaction));
英文:

There's a section of the bevy docs that covers this pretty well.
https://docs.rs/bevy/latest/bevy/input/struct.Input.html#multiple-systems

> In case multiple systems are checking for Input::just_pressed or Input::just_released but only one should react, for example in the case of triggering State change, you should consider clearing the input state, either by:
>
> Using Input::clear_just_pressed or Input::clear_just_released instead.
Calling Input::clear or Input::reset immediately after the state change.

Essentially you want to clear the input if you don't want other systems to receive that input event.

For your example:

if mouse.just_pressed(MouseButton::Left) {
  if let Some(position) = window.cursor_position() {
    spawn_tower(&amp;mut commands, *tower_type, &amp;assets, position.extend(0.));
    mouse.clear_just_pressed(MouseButton::Left); // &lt;- New line here
  }
}

You will also want to make sure this system runs before the other system so it blocks the input before the other system checks. For a complete guide on ordering systems checkout Bevy Cheatbook: Explicit System Ordering

It would be something like this:

app
    .add_system(tower_button_interaction)
    .add_system(tower_placement_system.after(tower_button_interaction));

huangapple
  • 本文由 发表于 2023年2月6日 20:48:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/75361510.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定