Skip to content
This repository has been archived by the owner on Jan 27, 2021. It is now read-only.

Add grabbing the mouse example #6

Merged
merged 1 commit into from
Sep 10, 2020
Merged

Add grabbing the mouse example #6

merged 1 commit into from
Sep 10, 2020

Conversation

nickbryan
Copy link
Contributor

I am hoping this is the correct way to grab the mouse then hide it. I know that there is an ongoing discussion in winit around this functionality so the API might change in the future.

@nickbryan
Copy link
Contributor Author

Hmm, I'm not actually sure this is the correct approach. This seems to prevent the CursorMoved.position from updating which prevents ui interaction etc.

@inodentry
Copy link
Owner

Well, i'd think this is "normal" (albeit perhaps surprising at first glance) behaviour? If you are grabbing the mouse, you don't really have a cursor anymore (unless you implement your own), only relative movement. This means you can't interact with UI, until you un-grab the mouse.

I think we just need to make this implication clear, and adjust the example to show grabbing and ungrabbing at runtime. Grabbing is not something that should just be done once and left that way. People will want to manage that state -- grab the mouse during gameplay and ungrab it when showing menus or other ui that needs a cursor for interacting with it.

@nickbryan
Copy link
Contributor Author

HI @jamadazi, thanks for the quick response.

Yeah, that does make sense. The work that I was doing which lead me to this was for some sort of RTS style game where I didn't want the mouse to be able to leave the window but function as normal so that the user could still interact with the UI (provided by Bevy) and other components that use the mouse position to check interactions and such. When I locked the mouse with this method I was surprised to see that the positions no longer update but it does make sense when I think about it.

Further digging allowed me to figure out that there are two types of cursor grab. Confine (is what I was after) which stops the mouse from going outside of the window and Lock which locks the cursor in place which is what we are seeing here. It turns out that winit does not support the Confine part on macOS yet which is why I was getting this behaviour. On Windows, this will work as expected and the mouse will be Confined to the window not locked.

When I get some time I will update the example to lock and and unlock based on a key press and outline some of the constraints listed above. Do you think this will suffice?

@inodentry
Copy link
Owner

Yeah, makes sense, we had different expectations. I was thinking in terms of a FPS game, so the "locking" behaviour was what I expected.

For the example, I think it should be locking/unlocking based on receiving an event, because that would probably be the most useful to most people (just send events from other systems to trigger the lock/unlock). Create a new event type like enum MouseLock { Locked, Unlocked }.

@inodentry
Copy link
Owner

BTW, for your use case, I have some ideas. Perhaps, instead of relying on the "confine" functionality, you could instead lock the mouse and implement your own in-game cursor. I suspect that if just fire your own CursorMoved events based on your "virtual cursor", things should just work. This has the benefit of giving you full control over the cursor's position and movement.

@nickbryan
Copy link
Contributor Author

😮 I can not believe I didn't think about dispatching my own CursorMoved events. That makes so much sense! I will have to try this today. Yeah, what you suggested with my own in-game cursor was pretty much what I had in mind but could not think of how to get the UI stuff working (with it using the CursorMoved events). You are a lifesaver!

I will get this example updated then as you suggested. Thanks for the help!

@nickbryan
Copy link
Contributor Author

Actually, thinking about this a bit further, I am not 100% sure that this would work as a CursorMoved event would be fired with the current position of the mouse every time it is moved and also another CursorMoved event by my code. As far as I am aware, there is no way to stop the current CursorMoved events from triggering which means that you would need to somehow ignore the first even that is fired from the window API.

@nickbryan
Copy link
Contributor Author

I have updated the PR to be event-driven. Please let me know your thoughts :)

@nickbryan
Copy link
Contributor Author

For the record, I actually managed to work around the event propagation issue described above with the following:

...other_app_init_stuff()
.add_stage_before(stage::EVENT_UPDATE, CURSOR_STAGE)
.add_system_to_stage(CURSOR_STAGE, cancel_cursor_event_system.system())

pub const CURSOR_STAGE: &str = "cursor_stage";

fn cancel_cursor_event_system(mut events: ResMut<Events<CursorMoved>>) {
    events.clear();
}

Then I should be able to add publish my CursorEvent's from this stage and everything should hopefully work fine =)

@inodentry
Copy link
Owner

I want to keep the code snippets in the cookbook short and clear, to not distract with "extra" things that are not directly related to the task at hand. Minimalistic and straight to the point. The cookbook is for concise snippets to show the relevant code for specific tasks, not for complete runnable/compilable examples.

The key input feels unnecessary.

I think we should only merge these parts of the code: MouseLockEvent, MouseCaptureState and mouse_capture_system. We should remove LockSwitchState and lock_switch_system.

You can also remove the main function. It is not doing anything out of the ordinary, and I expect anyone reading the cookbook to be familiar with the basics of bevy and to know that they need to register their stuff in the App builder. I don't want to include that with every example. I also assume everyone is importing the bevy prelude, so you can remove the first use line at the top. Only include imports for things not in the prelude.

Also please add a brief note about what the example is doing (something like "Example system to lock/unlock the mouse, triggered by a custom event"), just so that people don't have to figure it out on their own. You can put that before the code, or as a doc comment inside the code.

Also BTW, if you have figured out how to do the "virtual cursor" in your game, it would be nice if you could make a separate PR to contribute an example for that, too.

@nickbryan
Copy link
Contributor Author

All really good points. I wasn't sure how far to go with it and hopefully what I have pushed does the trick?

I would be more than happy to add something for that once I have it all worked out. I imagine the example might get pretty large though. Maybe I could give you a shout on Discord and discuss what should go in the example when I get it working?

@inodentry
Copy link
Owner

Thank you for cooperating with me to improve the quality of your contribution. :)

I will merge this now. It looks good.

I will just do a quick edit to move the comment you added about what the example does, to be outside of the example. I think I would like it more that way.

I will do that myself, so that I don't waste your time by going back and forth for some minor edits.

I wasn't sure how far to go with it

When in doubt, less is more. I like the minimalist philosophy. "Perfection is achieved not when there is nothing more to add, but when there is nothing more to remove." ;)

Ideally, I want each example to show the minimal amount of code that people would need to understand how to implement the desired feature in a useful way.

I would be more than happy to add something for that once I have it all worked out. I imagine the example might get pretty large though.

Yes, no rush. :) When you get to a point where you feel like you know what you are doing, just have a think about how to reduce what you have down to something concise and minimal.

Maybe I could give you a shout on Discord and discuss what should go in the example when I get it working?

Sure. You can contact me in chat.

@inodentry inodentry merged commit 069d6e8 into inodentry:master Sep 10, 2020
@nickbryan
Copy link
Contributor Author

When in doubt, less is more. I like the minimalist philosophy. "Perfection is achieved not when there is nothing more to add, but when there is nothing more to remove." ;)

I really like this! its a good philosophy!

Thanks for making the amends and for the merge. It's been a pleasure dealing with you on this one and I think what you are doing with the cookbook and the cheatsheet is spot on. Things like this need to be well curated so stay opinionated and keep up the awesome work.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants