feat: rebinding example
This commit is contained in:
+4601
File diff suppressed because it is too large
Load Diff
+30
@@ -0,0 +1,30 @@
|
|||||||
|
[package]
|
||||||
|
name = "rebinding"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target-dir = "../../../../target"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
x11 = ["bevy/x11"]
|
||||||
|
wayland = ["bevy/wayland"]
|
||||||
|
windows = ["bevy/bevy_winit"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bevy = { version = "0.16.1", default-features = false, features = [
|
||||||
|
"bevy_asset",
|
||||||
|
"bevy_text",
|
||||||
|
"bevy_state",
|
||||||
|
"bevy_ui",
|
||||||
|
"bevy_window",
|
||||||
|
"bevy_winit",
|
||||||
|
"default_font",
|
||||||
|
] }
|
||||||
|
bevy_controls = { path = "./../../" }
|
||||||
|
bevy_controls_derive = { path = "./../../../bevy_controls_derive" }
|
||||||
|
strum = "0.26.2"
|
||||||
|
strum_macros = "0.26.2"
|
||||||
|
|
||||||
|
[target.x86_64-pc-windows-msvc]
|
||||||
|
linker = "rust-lld.exe"
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
# Rebinding example
|
||||||
|
|
||||||
|
Run from this directory with one platform feature:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run --features x11
|
||||||
|
cargo run --features wayland
|
||||||
|
cargo run --features windows
|
||||||
|
```
|
||||||
|
|
||||||
|
Controls:
|
||||||
|
|
||||||
|
- `1`-`4`: select Left, Back, Forward, or Right.
|
||||||
|
- `R`: capture next key and add it to selected action.
|
||||||
|
- `C`: clear selected action bindings.
|
||||||
|
- `Escape`: cancel key capture.
|
||||||
|
|
||||||
|
Default WASD bindings and captured keys use physical `KeyCode` positions.
|
||||||
+207
@@ -0,0 +1,207 @@
|
|||||||
|
use bevy::{
|
||||||
|
input::{keyboard::KeyboardInput, ButtonState},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
use bevy_controls::{
|
||||||
|
contract::InputsContainer,
|
||||||
|
plugin::ControlsPlugin,
|
||||||
|
resource::{Binding, BindingConfig, Bindings, Controls, InputType, Keyboard, PlayerActions},
|
||||||
|
};
|
||||||
|
use bevy_controls_derive::{Action, GameState};
|
||||||
|
use strum_macros::EnumIter;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Hash, EnumIter, Clone, Copy, Debug, Action)]
|
||||||
|
enum MovementAction {
|
||||||
|
Left,
|
||||||
|
Back,
|
||||||
|
Forward,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MovementAction {
|
||||||
|
const fn name(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::Left => "Left",
|
||||||
|
Self::Back => "Back",
|
||||||
|
Self::Forward => "Forward",
|
||||||
|
Self::Right => "Right",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(States, PartialEq, Eq, Clone, Hash, Debug, Default, GameState)]
|
||||||
|
enum DemoState {
|
||||||
|
#[default]
|
||||||
|
Playing,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource, Default, Clone, Debug)]
|
||||||
|
struct MovementInputs(PlayerActions<MovementAction>);
|
||||||
|
|
||||||
|
impl InputsContainer<MovementAction> for MovementInputs {
|
||||||
|
fn iter_inputs<'a>(&'a self) -> Box<dyn Iterator<Item = &'a PlayerActions<MovementAction>> + 'a> {
|
||||||
|
Box::new(std::iter::once(&self.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn me<'a>(&'a self) -> Option<&'a PlayerActions<MovementAction>> {
|
||||||
|
Some(&self.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn me_mut<'a>(&'a mut self) -> Option<&'a mut PlayerActions<MovementAction>> {
|
||||||
|
Some(&mut self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
struct RebindingUi {
|
||||||
|
selected: MovementAction,
|
||||||
|
capturing: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RebindingUi {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
selected: MovementAction::Left,
|
||||||
|
capturing: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct RebindingText;
|
||||||
|
|
||||||
|
const TEXT_SIZE: f32 = 30.;
|
||||||
|
|
||||||
|
fn physical_binding(key_code: KeyCode) -> Binding<DemoState> {
|
||||||
|
Binding::from_single(InputType::Keyboard(Keyboard::KeyCode(key_code)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn controls() -> Controls<MovementAction, DemoState> {
|
||||||
|
Controls::new()
|
||||||
|
.with(
|
||||||
|
MovementAction::Left,
|
||||||
|
BindingConfig::new(Bindings::new(vec![physical_binding(KeyCode::KeyA)]).customizable()),
|
||||||
|
)
|
||||||
|
.with(
|
||||||
|
MovementAction::Back,
|
||||||
|
BindingConfig::new(Bindings::new(vec![physical_binding(KeyCode::KeyS)]).customizable()),
|
||||||
|
)
|
||||||
|
.with(
|
||||||
|
MovementAction::Forward,
|
||||||
|
BindingConfig::new(Bindings::new(vec![physical_binding(KeyCode::KeyW)]).customizable()),
|
||||||
|
)
|
||||||
|
.with(
|
||||||
|
MovementAction::Right,
|
||||||
|
BindingConfig::new(Bindings::new(vec![physical_binding(KeyCode::KeyD)]).customizable()),
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
App::new()
|
||||||
|
.add_plugins((
|
||||||
|
DefaultPlugins.set(WindowPlugin {
|
||||||
|
primary_window: Some(Window {
|
||||||
|
title: "Bevy Controls Rebinding".into(),
|
||||||
|
..default()
|
||||||
|
}),
|
||||||
|
..default()
|
||||||
|
}),
|
||||||
|
ControlsPlugin::<MovementAction, MovementInputs, DemoState>::new(controls()),
|
||||||
|
))
|
||||||
|
.init_resource::<RebindingUi>()
|
||||||
|
.add_systems(Startup, setup)
|
||||||
|
.add_systems(Update, (handle_rebinding, update_text))
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup(mut commands: Commands) {
|
||||||
|
commands.spawn(Camera2d);
|
||||||
|
commands.spawn((
|
||||||
|
Text::new(""),
|
||||||
|
TextFont {
|
||||||
|
font_size: TEXT_SIZE,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
TextColor(Color::WHITE),
|
||||||
|
BackgroundColor(Color::BLACK),
|
||||||
|
RebindingText,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_rebinding(
|
||||||
|
mut controls: ResMut<Controls<MovementAction, DemoState>>,
|
||||||
|
mut ui: ResMut<RebindingUi>,
|
||||||
|
keys: Res<ButtonInput<KeyCode>>,
|
||||||
|
mut keyboard_events: EventReader<KeyboardInput>,
|
||||||
|
) {
|
||||||
|
if ui.capturing {
|
||||||
|
for event in keyboard_events.read() {
|
||||||
|
if event.state != ButtonState::Pressed {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if event.key_code == KeyCode::Escape {
|
||||||
|
ui.capturing = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let binding = Binding::from_single(InputType::Keyboard(Keyboard::KeyCode(event.key_code)));
|
||||||
|
controls.push_binding(ui.selected, binding);
|
||||||
|
ui.capturing = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in keyboard_events.read() {}
|
||||||
|
|
||||||
|
if keys.just_pressed(KeyCode::Digit1) {
|
||||||
|
ui.selected = MovementAction::Left;
|
||||||
|
}
|
||||||
|
if keys.just_pressed(KeyCode::Digit2) {
|
||||||
|
ui.selected = MovementAction::Back;
|
||||||
|
}
|
||||||
|
if keys.just_pressed(KeyCode::Digit3) {
|
||||||
|
ui.selected = MovementAction::Forward;
|
||||||
|
}
|
||||||
|
if keys.just_pressed(KeyCode::Digit4) {
|
||||||
|
ui.selected = MovementAction::Right;
|
||||||
|
}
|
||||||
|
if keys.just_pressed(KeyCode::KeyC) {
|
||||||
|
controls.clear_bindings(ui.selected);
|
||||||
|
}
|
||||||
|
if keys.just_pressed(KeyCode::KeyR) {
|
||||||
|
ui.capturing = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_text(
|
||||||
|
mut text: Single<&mut Text, With<RebindingText>>,
|
||||||
|
ui: Res<RebindingUi>,
|
||||||
|
controls: Res<Controls<MovementAction, DemoState>>,
|
||||||
|
) {
|
||||||
|
let bindings = controls.get(ui.selected).map_or_else(
|
||||||
|
|| String::from(" (none)"),
|
||||||
|
|config| {
|
||||||
|
config
|
||||||
|
.bindings
|
||||||
|
.iter()
|
||||||
|
.map(|binding| format!(" {binding:?}"))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n")
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let capture_status = if ui.capturing {
|
||||||
|
"Waiting for a key. Escape cancels."
|
||||||
|
} else {
|
||||||
|
"Ready. Press R to add a binding."
|
||||||
|
};
|
||||||
|
|
||||||
|
text.0 = format!(
|
||||||
|
"Keyboard rebinding demo\n\nSelected action: {}\n{}\n\nBindings:\n{}\n\n1-4 select action | R capture | C clear | Escape cancel",
|
||||||
|
ui.selected.name(),
|
||||||
|
capture_status,
|
||||||
|
bindings,
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user