Bevy Version: | 0.14 | (outdated!) |
---|
As this page is outdated, please refer to Bevy's official migration guides while reading, to cover the differences: 0.14 to 0.15.
I apologize for the inconvenience. I will update the page as soon as I find the time.
Keyboard Input
Relevant official examples:
keyboard_input
,
keyboard_input_events
.
This page shows how to handle keyboard keys being pressed and released.
Note: Command Key on Mac corresponds to the Super/Windows Key on PC.
Similar to mouse buttons, keyboard input is available
as a ButtonInput
resource, events, and run
conditions (see list). Use
whichever pattern feels most appropriate to your use case.
Checking Key State
Most commonly for games, you might be interested in specific known keys and
detecting when they are pressed or released. You can check specific keys
using the ButtonInput<KeyCode>
resource.
- Use
.pressed(…)
/.released(…)
to check if a key is being held down- These return
true
every frame, for as long as the key is in the respective state.
- These return
- Use
.just_pressed(…)
/.just_released(…)
to detect the actual press/release- These return
true
only on the frame update when the press/release happened.
- These return
fn keyboard_input(
keys: Res<ButtonInput<KeyCode>>,
) {
if keys.just_pressed(KeyCode::Space) {
// Space was pressed
}
if keys.just_released(KeyCode::ControlLeft) {
// Left Ctrl was released
}
if keys.pressed(KeyCode::KeyW) {
// W is being held down
}
// we can check multiple at once with `.any_*`
if keys.any_pressed([KeyCode::ShiftLeft, KeyCode::ShiftRight]) {
// Either the left or right shift are being held down
}
if keys.any_just_pressed([KeyCode::Delete, KeyCode::Backspace]) {
// Either delete or backspace was just pressed
}
}
To iterate over any keys that are currently held, or that have been pressed/released:
fn keyboard_iter(
keys: Res<ButtonInput<KeyCode>>,
) {
for key in keys.get_pressed() {
println!("{:?} is currently held down", key);
}
for key in keys.get_just_pressed() {
println!("{:?} was pressed", key);
}
for key in keys.get_just_released() {
println!("{:?} was released", key);
}
}
Run Conditions
Another workflow is to add run conditions to your systems, so that they only run when the appropriate inputs happen.
It is highly recommended you write your own run conditions, so that you can check for whatever you want, support configurable bindings, etc…
For prototyping, Bevy offers some built-in run conditions:
use bevy::input::common_conditions::*;
app.add_systems(Update, (
handle_jump
.run_if(input_just_pressed(KeyCode::Space)),
handle_shooting
.run_if(input_pressed(KeyCode::Enter)),
));
Keyboard Events
To get all keyboard activity, you can use KeyboardInput
events:
fn keyboard_events(
mut evr_kbd: EventReader<KeyboardInput>,
) {
for ev in evr_kbd.read() {
match ev.state {
ButtonState::Pressed => {
println!("Key press: {:?} ({:?})", ev.key_code, ev.logical_key);
}
ButtonState::Released => {
println!("Key release: {:?} ({:?})", ev.key_code, ev.logical_key);
}
}
}
}
Physical KeyCode
vs. Logical Key
When a key is pressed, the event contains two important pieces of information:
- The
KeyCode
, which always represents a specific key on the keyboard, regardless of the OS layout or language settings. - The
Key
, which contains the logical meaning of the key as interpreted by the OS.
When you want to implement gameplay mechanics, you want to use the KeyCode
.
This will give you reliable keybindings that always work, including for multilingual
users with multiple keyboard layouts configured in their OS.
When you want to implement text/character input, you want to use the Key
.
This can give you Unicode characters that you can append to your text string and
will allow your users to type just like they do in other applications.
If you'd like to handle special function keys or media keys on keyboards that
have them, that can also be done via the logical Key
.
Text Input
Here is a simple example of how to implement text input into a string (here stored as a local).
use bevy::input::ButtonState;
use bevy::input::keyboard::{Key, KeyboardInput};
fn text_input(
mut evr_kbd: EventReader<KeyboardInput>,
mut string: Local<String>,
) {
for ev in evr_kbd.read() {
// We don't care about key releases, only key presses
if ev.state == ButtonState::Released {
continue;
}
match &ev.logical_key {
// Handle pressing Enter to finish the input
Key::Enter => {
println!("Text input: {}", &*string);
string.clear();
}
// Handle pressing Backspace to delete last char
Key::Backspace => {
string.pop();
}
// Handle key presses that produce text characters
Key::Character(input) => {
// Ignore any input that contains control (special) characters
if input.chars().any(|c| c.is_control()) {
continue;
}
string.push_str(&input);
}
_ => {}
}
}
}
Note how we implement special handling for keys like Backspace
and Enter
.
You can easily add special handling for other keys that make sense in your
application, like arrow keys or the Escape
key.
Keys that produce useful characters for our text come in as small Unicode
strings. It is possible that there might be more than one char
per keypress
in some languages.
Note: To support text input for international users who use languages with complex scripts (such as East Asian languages), or users who use assistive methods like handwriting recognition, you also need to support IME input, in addition to keyboard input.
Keyboard Focus
If you are doing advanced things like caching state to detect multi-key sequences or combinations of keys, you might end up in an inconsistent state if the Bevy OS window loses focus in the middle of keyboard input, such as with Alt-Tab or similar OS window switching mechanisms.
If you are doing such things and you think your algorithm might be getting
stuck, Bevy offers a KeyboardFocusLost
event to let you
know when you should reset your state.
use bevy::input::keyboard::KeyboardFocusLost;
fn detect_special_sequence(
mut evr_focus_lost: EventReader<KeyboardFocusLost>,
mut remembered_keys: Local<Vec<KeyCode>>,
) {
// Imagine we need to remeber a sequence of keypresses
// for some special gameplay reason.
// TODO: implement that; store state in `remembered_keys`
// But it might go wrong if the user Alt-Tabs, we need to reset
if !evr_focus_lost.is_empty() {
remembered_keys.clear();
evr_focus_lost.clear();
}
}