Reading the Position of a Mouse click in Bevy
Asked Answered
S

2

11

I am trying to see where the mouse is pressed so I can select my character. I have tried the following

#[derive(Default)]
struct State { // Set up from example
    mouse_button_event_reader: EventReader<MouseButtonInput>,
    cursor_moved_event_reader: EventReader<CursorMoved>,
}

fn select_character(
    mut state: ResMut<State>,
    mouse_button_input_events: Res<Events<MouseButtonInput>>,
    cursor_moved_events: Res<Events<CursorMoved>>,
) {

    for (cursor_event, mouse_event) in state
        .cursor_moved_event_reader
        .iter(&cursor_moved_events)
        .zip(
            state
                .mouse_button_event_reader
                .iter(&mouse_button_input_events),
        )
    {
        println!("{:?}", cursor_event);
        println!("{:?}", mouse_event);
    }
}

This kind of works, but the mouse needs to be moving while it is clicked. Is there a way of getting the position once the mouse is pressed?

Edit:

I thought .find_latest might work, getting it to return the latest Some value.

for event in state
        .mouse_button_event_reader
        .iter(&mouse_button_input_events)
    {
        let cursor_event = state
            .cursor_moved_event_reader
            .find_latest(&cursor_moved_events, |x| x.is_Some() // Not sure how to only return values that are Some(x)
        );
        println!("{:?}", event);
        println!("{:?}", cursor_event);
    }
Seagrave answered 22/8, 2020 at 23:2 Comment(0)
N
7

It appears that .find_latest is for finding the next unread value. Which means you would probably also require movement. Since, it is likely is a click was the last event, the movement was already captured.

Now, I can't promise that this is idiomatic since we're all new at this, but there is a solution:

Add two floating-point variables to your state-struct. Then, when the cursor is moved, store that position. When the mouse is clicked, recall that information. Since it becomes a part of the resource (state) that information will be available for read and write.

Implemented

#[derive(Default)]
struct State { // Set up from example
    mouse_button_event_reader: EventReader<MouseButtonInput>,
    cursor_moved_event_reader: EventReader<CursorMoved>,
}

struct MouseLoc(Vec2);

fn select_character(
    mut state: ResMut<State>,
    mouse_pos: ResMut<MouseLoc>,
    mouse_button_input_events: Res<Events<MouseButtonInput>>,
) {
    for event in state
        .mouse_button_event_reader
        .iter(&mouse_button_input_events)
    {
        println!("event: {:?} position: {:?}", event, mouse_pos.0);
    }
}

fn mouse_movement_updating_system(
    mut mouse_pos: ResMut<MouseLoc>,
    mut state: ResMut<State>,
    cursor_moved_events: Res<Events<CursorMoved>>,
) {
    for event in state.cursor_moved_event_reader.iter(&cursor_moved_events) {
        mouse_pos.0 = event.position;
    }
}

fn main() {
    App::build()
        .add_default_plugins()
        ...
        .add_resource(MouseLoc(Vec2::new(0.0, 0.0)))
        .add_system(mouse_movement_updating_system.system());
        .add_system(position_mouse_click_system.system());
        ...
        .run();
}
Notarize answered 23/8, 2020 at 1:45 Comment(1)
Thanks for the answer, I'll look into it in the morning and let you know.Seagrave
S
7

I think what you want is something like this:

fn handle_mouse_clicks(mouse_input: Res<Input<MouseButton>>, windows: Res<Windows>) {
    let win = windows.get_primary().expect("no primary window");
    if mouse_input.just_pressed(MouseButton::Left) {
        println!("click at {:?}", win.cursor_position());
    }
}

This works for version 0.5.0 of bevy: https://docs.rs/bevy/0.5.0/bevy/window/struct.Window.html#method.cursor_position

You could implement your own object always tracking the current position (via an event reader) as well, but in my tests, the method described above gives a more (temporal) accurate result (you get the position at the time the click comes through not an arbitrarily old position) - but I am not an expert on this!

Satisfaction answered 8/4, 2021 at 18:2 Comment(2)
This is the method I use in my game, it works perfectly.Splenomegaly
update: still works in bevy 0.6.0: docs.rs/bevy/latest/bevy/window/…Satisfaction

© 2022 - 2024 — McMap. All rights reserved.