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<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 also tried changing the if mouse.just_pressed(MouseButton::Left)
to if matches!(interaction, Interaction::Clicked)
, but the same thing happened.
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(&mut commands, *tower_type, &assets, position.extend(0.));
mouse.clear_just_pressed(MouseButton::Left); // <- 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));