Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Input combination / modifier keys abstraction #1654

Open
alice-i-cecile opened this issue Mar 14, 2021 · 7 comments
Open

Input combination / modifier keys abstraction #1654

alice-i-cecile opened this issue Mar 14, 2021 · 7 comments
Labels
A-Input Player input via keyboard, mouse, gamepad, and more C-Feature A new feature, making something new possible C-Usability A targeted quality-of-life change that makes Bevy easier to use S-Needs-Design-Doc This issue or PR is particularly complex, and needs an approved design doc before it can be merged

Comments

@alice-i-cecile
Copy link
Member

What problem does this solve or what need does it fill?

Modifier keys are very commonly used in both games and applications. The existing approach to this is very manual, and heavy on boilerplate. See the cheatbook page for a good overview of how to work with input right now.

What solution would you like?

Expose a first-class solution for modifier keys in bevy_input. I'm not immediately sure what this would look like, so suggestions are very welcome.

What alternative(s) have you considered?

Use match statements everywhere, repeatedly. Iterate through eventreader (because that says it is in order) and maintain my own modifier key state as a local resource (credit to @mattdm on Discord.

Additional context

None.

@alice-i-cecile alice-i-cecile added C-Feature A new feature, making something new possible D-Trivial Nice and easy! A great choice to get started with Bevy A-Input Player input via keyboard, mouse, gamepad, and more C-Usability A targeted quality-of-life change that makes Bevy easier to use labels Mar 14, 2021
@mattdm
Copy link

mattdm commented Mar 14, 2021

Yeah, thanks. My current sample code looks like this:

struct KeyboardModifierState {
    shift: ElementState,
    alt: ElementState,
    control: ElementState,
}

impl Default for KeyboardModifierState {
    fn default() -> Self {
        Self {
            shift: ElementState::Released,
            alt: ElementState::Released,
            control: ElementState::Released,
        }
    }
}

fn keyboard_input_system(
    mut modifier_state: Local<KeyboardModifierState>,
    mut eventreader_keyboard: Local<EventReader<KeyboardInput>>,
    keyboard_input_events: Res<Events<KeyboardInput>>,
) {
    for keyevent in eventreader_keyboard.iter(&keyboard_input_events) {
        //println!("{:?}", keyevent);
        match keyevent {
            KeyboardInput {
                key_code: Some(KeyCode::LShift),
                state,
                ..
            }
            | KeyboardInput {
                key_code: Some(KeyCode::RShift),
                state,
                ..
            } => modifier_state.shift = state.clone(),
            KeyboardInput {
                key_code: Some(KeyCode::LAlt),
                state,
                ..
            }
            | KeyboardInput {
                key_code: Some(KeyCode::RAlt),
                state,
                ..
            } => modifier_state.alt = state.clone(),
            KeyboardInput {
                key_code: Some(KeyCode::LControl),
                state,
                ..
            }
            | KeyboardInput {
                key_code: Some(KeyCode::RControl),
                state,
                ..
            } => modifier_state.control = state.clone(),
            KeyboardInput {
                key_code: Some(KeyCode::A),
                state: ElementState::Pressed,
                ..
            } => println!(
                "A with Shift {:?} Alt {:?} Control {:?}",
                modifier_state.shift, modifier_state.alt, modifier_state.control
            ),
            _ => {
                // ignore all else
            }
        }
    }
}

@cart
Copy link
Member

cart commented Mar 14, 2021

This has come up previously (#989 (comment)). Unless I'm missing a use case, I think Bevy Input<KeyCode> already handles this reasonably nicely?

fn process_input(input: Res<Input<KeyCode>>) {
    let shift = input.pressed(KeyCode::LShift) || input.pressed(KeyCode::RShift);
    let ctrl = input.pressed(KeyCode::LControl) || input.pressed(KeyCode::RControl);
    if ctrl && shift && input.pressed(KeyCode::A) {
       println!("ctrl + shift + a");
    }
}

@alice-i-cecile
Copy link
Member Author

Ah, I missed it! Thanks; I'll close this out.

bors bot pushed a commit that referenced this issue Mar 14, 2021
This PR adds a small example that shows how to use Keyboard modifiers, as shown in [this](#1654 (comment)) snippet.

Fixes #1656.

Co-authored-by: guimcaballero <[email protected]>
@mattdm
Copy link

mattdm commented Mar 15, 2021

This has come up previously (#989 (comment)). Unless I'm missing a use case, I think Bevy Input<KeyCode> already handles this reasonably nicely?

@cart I don't think this is quite the same ... your code is "A and Shift are both pressed right now". But that doesn't handle "Shift was down and A was pressed at that time", which is significant because 1) in normal typing usage, holding Shift and then pressing A gives different results from holding A and pressing Shift, and 2) with my code, I get A presses registered when I first hit it and then if I hold I get repeats at the rate the OS is sending them. With your code, I get repeats as fast as the framerate.

I thought about using a FixedTimestep so I only get repeats every 0.1 seconds or whatever, but that doesn't work because it only fires if the keys are pressed on that time through. So with the "is currently pressed" approach, I'm moving from the ugly key match thing to a different complicated thing where I check what's pressed constantly but only actually do something about it 20 times a second (or whatever).

(My specific use case is WASD movement with shift to slow down and ctrl to speed up.)

The nicest would be to have the KeyboardInput event show the state of the modifiers at the time they were pressed. (See for example rust-sdl2.

@alice-i-cecile
Copy link
Member Author

This example only works if you care about instantaneous key combinations. It doesn't work quite like the keyboard does at a text input window. Fine for games in many cases, maybe not in others.

Reopening to see if we can address this more fully.

@RustyStriker
Copy link

@mattdm what are you trying to achieve? You have the just_pressed and just_released calls on the input, so you can do shift && input.just_pressed(key_code) to act once on the input, or hold it until input.just_released(key_code). As i think i understand what you eventually want is a bit wasteful(at least in my opinion) as you can easily work around this limitation rather easily.

@mattdm
Copy link

mattdm commented Mar 18, 2021

@mattdm what are you trying to achieve? You have the just_pressed and just_released calls on the input, so you can do shift && input.just_pressed(key_code) to act once on the input, or hold it until input.just_released(key_code). As i think i understand what you eventually want is a bit wasteful(at least in my opinion) as you can easily work around this limitation rather easily.

Try it; this won't work to get the same key-repeat behavior I describe or that my code which tracks the state does.

Also, this would be nice simply because it is similar to functionality offered by other frameworks -- see the rust-sdl2 link above -- which makes porting easier.

@alice-i-cecile alice-i-cecile added the S-Needs-Design-Doc This issue or PR is particularly complex, and needs an approved design doc before it can be merged label Apr 23, 2021
@alice-i-cecile alice-i-cecile removed the D-Trivial Nice and easy! A great choice to get started with Bevy label Jun 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Input Player input via keyboard, mouse, gamepad, and more C-Feature A new feature, making something new possible C-Usability A targeted quality-of-life change that makes Bevy easier to use S-Needs-Design-Doc This issue or PR is particularly complex, and needs an approved design doc before it can be merged
Projects
None yet
Development

No branches or pull requests

4 participants