Reading the Position of a Mouse click in Bevy - rust

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);
}

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();
}

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!

Related

Glutin on Rust: How do I passively track my mouse movement and position?

I want to track my cursor position as I work through my day to generate an artistic heat-map later.
To aggregate the data, I've pulled in glutin at version 0.29.1 and setup my loop with event matchers that tell me when I move my mouse and where.
The problem I have is that I want this aggregation to continue to work when I don't have the window focused. In the past, with other frameworks, I've been able to get around this kind of limitation by setting a maximized transparent window to always sit on top of all windows, but this hasn't worked for me.
How do I passively track my mouse movement and position with glutin (or another framework, preferably cross-platform so I don't have to dig into lower level apis with more obscure documentation)?
let event_loop = EventLoop::new();
let _window = WindowBuilder::new()
.with_decorations(false)
.with_maximized(true)
.with_always_on_top(true)
.with_transparent(true)
.build(&event_loop);
event_loop.run(move |event, _, _control_flow| match event {
Event::WindowEvent { event, .. } => match event {
WindowEvent::CursorMoved {
device_id,
position,
modifiers: _,
} => {
println!("{:?} {:?}", device_id, position);
}
WindowEvent::Focused(is_focused) => {
println!("focused::{}", is_focused);
}
_ => {}
},
Event::DeviceEvent { device_id, event } => match event {
DeviceEvent::MouseMotion { delta } => {
println!("{:?} {:?}", device_id, delta);
}
_ => {}
},
_ => {}
});
I was trying to hammer a square-peg into a round hole by forcing my usage of glutin to learn more along the lines of game-development. This is made trivial by using a more appropriate tool.
[dependencies]
device_query = "1.1.1"
use device_query::{DeviceState, DeviceEvents};
fn main() {
let device_state = DeviceState::new();
loop {
let mouse_state = device_state.get_mouse();
println!("{:?}", mouse_state.coords);
}
}
[Edit] or as recommended in the comments with a less resource intense callback, and an additional dependency on ctrlc = "3.2.3" to hold the application open indefinitely.
use device_query::{DeviceState, DeviceEvents};
use std::sync::mpsc::channel;
use ctrlc;
fn main() {
let device_state = DeviceState::new();
let _guard = device_state.on_mouse_move(|position| {
println!("{:?}", position);
});
let (tx, rx) = channel();
ctrlc::set_handler(move || tx.send(()).expect("Could not send signal on channel."))
.expect("Error setting Ctrl-C handler");
rx.recv().expect("Could not receive from channel.");
}

How to change Alpha in the various Sprite-types, in Bevy 0.8?

Bevy v0.8 has several "displayable-object" types, Sprite/SpriteBundle, SpriteSheetBundle, and MaterialMeshBundle. I would like to change the transparency (Alpha-channel, stored on Color Components) of, well, all of them - if possible.
The Answer Changing Sprite Color, addresses changing the Color of an object (where Color has the Alpha-channel) , but it only works for things that have a material associated. In Bevy v0.8, SpriteBundles don't have a material (they did in 0.5).
So is it possible to change the transparency of other objects, that don't have materials? (And if so, how?)
Fortunately, it is doable (and thanks to people in the Bevy-Discord channel for suggestions). There are two elements to the solution - first, all of the items I named, do have a Color component (although finding it can be tricky). Even a SpriteSheet, which is an Atlas of bitmaps, has one - presumably just for this usage. Second, the AnyOf filter, which gets any of the requested components, as a tuple of Options, each of which is either an object, or None.
// Create some disparate objects
pub fn do_add_sprites(mut commands: Commands, asset_server: Res<AssetServer>,
mut meshes: ResMut<Assets<Mesh>>, mut materials: ResMut<Assets<ColorMaterial>>,
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
) {
commands.spawn_bundle(SpriteSheetBundle {
transform: Transform::from_xyz(-300.0, -200.0, 1.0),
texture_atlas: texture_atlas_handle2, ..default()
})
.insert(IsMousing { is_dragging: false, is_hovering: false });
commands.spawn_bundle(MaterialMesh2dBundle {
transform: Transform::from_translation(Vec3::new(500.0, -200.0, 1.0)),
mesh: meshes.add(shape::RegularPolygon::new(50., 6).into()).into(),
material: materials.add(ColorMaterial::from(Color::BLUE)),
..default()})
.insert(IsMousing { is_dragging: false, is_hovering: false });
}
// Code elsewhere that says: if cursor over object, set is_hovering flag
pub fn apply_any_hovers(mut materials: ResMut<Assets<ColorMaterial>>,
mut any_hovercraft: Query<(Entity, &IsMousing,
AnyOf<(&mut TextureAtlasSprite, &Handle<ColorMaterial>)>,)>,
) {
for (_entity_id, mouser_data, some_object) in any_hovercraft.iter_mut() {
if mouser_data.is_hovering {
if some_object.0.is_some() {
let some_color = &mut some_object.0.unwrap().color;
some_color.set_a(0.5);
}
if some_object.1.is_some() {
let some_handle = some_object.1.unwrap();
let some_color = &mut materials.get_mut(some_handle).unwrap().color;
some_color.set_a(0.5);
}
} else {
if some_object.0.is_some() {
let some_color = &mut some_object.0.unwrap().color;
some_color.set_a(1.0);
}
if some_object.1.is_some() {
let some_handle = some_object.1.unwrap();
let some_color = &mut materials.get_mut(some_handle).unwrap().color;
some_color.set_a(1.0);
}
}
}
}

How do I create an "if-let" block for a gtk call?

I am trying to create an application that is using gtk to display a window on the desktop. I am trying to improve the error handling for the code by using "if-let" instead of using "unwrap()" calls. The code right now is:
sender.send(AppEvent::QUIT).unwrap();
and hoping to turn it into an if-let block
if let Some(<need_help_here>) = sender.send(AppEvent::QUIT);
I need help with the <need_help_here> part. The send function is defined as
pub fn send(&self, t: T) -> Result<(), mpsc::SendError<T>>
Since the function returns a Result and not an Option, you need to match on Ok(...), not on Some(...):
if let Ok(_) = sender.send(AppEvent::QUIT) {
println!("send successuful");
} else {
println!("send failed");
}
Note that _ discards the success "value" because it's always (), a void "unit" value carrying no information. Ok(()) would work as well.
The block should be
if Err(e) = sender.send(AppEvent::QUIT) {
println!("{:?}", e);
}

How Not to mix progress bar data with other output?

I have a little stacked with a progress bars.
This is a example program:
use indicatif::*;
use std::{sync::mpsc, sync::mpsc::*, thread};
pub enum Output {
Finish,
Tick,
}
fn main() {
let (tx, rx) = mpsc::channel::<Output>();
let join_handle = thread::spawn(move || {
handle_output_messages(100_000, rx);
});
(0..100_000).for_each(|_| tx.send(Output::Tick).unwrap());
tx.send(Output::Finish).unwrap();
join_handle.join().unwrap();
}
pub fn handle_output_messages(total_things: u64, rx: Receiver<Output>) {
let multi_bar = MultiProgress::new();
let progress_bar = multi_bar.add(ProgressBar::new(total_things));
thread::spawn(move || {
for output in rx {
match output {
Output::Tick => progress_bar.inc(1),
Output::Finish => {
progress_bar.finish();
(0..600).for_each(|x| println!("{:?}", x));
}
}
}
});
multi_bar.join_and_clear().unwrap();
}
if you try to run it, you will see, that progress bar messages will be mixed with iterated numbers. This it not good for me, but I have no any idea, how to fix this behavior.
Maybe anybody can help me?
Using the standard println! with the progress bars isn't going to work, since they're going to overwrite eachother. Instead, use ProgressBar::println, which prints the message above the progress bar without overwriting it.

How to correctly exit the thread blocking on mpsc::Receiver

impl A {
fn new() -> (A, std::sync::mpsc::Receiver<Data>) {
let (sender, receiver) = std::sync::mpsc::channel();
let objA = A { sender: sender, }; // A spawns threads, clones and uses sender etc
(objA, receiver)
}
}
impl B {
fn new() -> B {
let (objA, receiver) = A::new();
B {
a: objA,
join_handle: Some(std::thread::spwan(move || {
loop {
match receiver.recv() {
Ok(data) => /* Do Something, inform main thread etc */,
Err(_) => break,
}
}
})),
}
}
}
impl Drop for B {
fn drop(&mut self) {
// Want to do something like "sender.close()/receiver.close()" etc so that the following
// thread joins. But there is no such function. How do i break the following thread ?
self.join_handle().take().unwrap().join().unwrap();
}
}
Is there a way to cleanly exit under such a circumstance ? The thing is that when either receiver or sender is dropped the other sniffs this and gives an error. In case of receiver it will be woken up and will yield an error in which case i am breaking out of the infinite and blocking loop above. However how do i do that explicitly using this very property of channels, without resorting to other flags in conjunction with try_recv()etc., and cleanly exit my thread deterministically?
Why not sending a specific message to shut this thread? I do not know what is your data but most of the time it may be an enum and adding a enum variant like 'MyData::Shutdown' in your receive you can simply break out of the loop.
You can wrap the a field of your B type in an Option. This way in the Drop::drop method you can do drop(self.a.take()) which will replace the field with a None and drop the sender. This closes the channel and your thread can now be properly joined.
You can create a new channel and swap your actual sender out with the dummy-sender. Then you can drop your sender and therefor join the thread:
impl Drop for B {
fn drop(&mut self) {
let (s, _) = channel();
drop(replace(&mut self.a.sender, s));
self.join_handle.take().unwrap().join().unwrap();
}
}
Try it out in the playpen: http://is.gd/y7A9L0
I don't know what the overhead of creating and immediately dropping a channel is, but it's not free and unlikely to be optimized out (There's an Arc in there).
on a side-note, Your infinite loop with a match on receiver.recv() could be replaced by a for loop using the Receiver::iter method:
for _ in receiver.iter() {
// do something with the value
}

Resources