feat: possibility define BindConfig in plugin setup

This commit is contained in:
2024-03-12 10:17:34 +01:00
parent e1e762395f
commit 234e086a77
2 changed files with 110 additions and 69 deletions
+27 -38
View File
@@ -9,7 +9,10 @@ use bevy::{ecs::schedule::States, prelude::*};
use bevy_controls::{ use bevy_controls::{
contract::InputsContainer, contract::InputsContainer,
plugin::ControlsPlugin, plugin::ControlsPlugin,
resource::{Binding, ButtonCombination, Controls, InputType, InputValue, Keyboard, PlayerInputs}, resource::{
ActivationOptions, Binding, BindingConfig, Bindings, ButtonCombination, Controls, InputType,
InputValue, Keyboard, OptionsMode, PlayerInputs,
},
}; };
use bevy_controls_derive::{Action, GameState}; use bevy_controls_derive::{Action, GameState};
use strum_macros::EnumIter; use strum_macros::EnumIter;
@@ -68,68 +71,54 @@ fn main() {
}), }),
ControlsPlugin::<MyAction, MyInputsContainer, MyGameState>::new( ControlsPlugin::<MyAction, MyInputsContainer, MyGameState>::new(
Controls::<MyAction, MyGameState>::new() Controls::<MyAction, MyGameState>::new()
// the first control instance I will describe in detail
.with( .with(
MyAction::Left, MyAction::Left,
// this way input will work only on latin layout only on `A` BindingConfig::new(Bindings::new(vec![
// this way input will work only on latin layout only on `A`, because it uses `Keyboard::KeyCode`
Binding::new(ButtonCombination::Single(InputType::Keyboard( Binding::new(ButtonCombination::Single(InputType::Keyboard(
Keyboard::KeyCode(KeyCode::A), Keyboard::KeyCode(KeyCode::A),
))), ))),
)
.with(
MyAction::Left,
// this way input will work only on any layout on same button, 35 is `H` for `us` layout or // this way input will work only on any layout on same button, 35 is `H` for `us` layout or
// `О` for `ru` layout // `О` for `ru` layout, because it uses `Keyboard::ScanCode`
Binding::new(ButtonCombination::Single(InputType::Keyboard( Binding::new(ButtonCombination::Single(InputType::Keyboard(
Keyboard::ScanCode(ScanCode(35)), Keyboard::ScanCode(ScanCode(35)),
))), ))),
])),
) )
// the rest will be simplified
.with( .with(
MyAction::Back, MyAction::Back,
Binding::new(ButtonCombination::Single(InputType::Keyboard( BindingConfig::from_vec(vec![
Keyboard::KeyCode(KeyCode::S), Binding::from_single(InputType::from_key_code(KeyCode::S)),
))), Binding::from_single(InputType::from_scan_code(ScanCode(36))),
) ]),
.with(
MyAction::Back,
Binding::new(ButtonCombination::Single(InputType::Keyboard(
Keyboard::ScanCode(ScanCode(36)),
))),
) )
.with( .with(
MyAction::Forward, MyAction::Forward,
Binding::new(ButtonCombination::Single(InputType::Keyboard( BindingConfig::from_vec(vec![
Keyboard::KeyCode(KeyCode::W), Binding::from_single(InputType::from_key_code(KeyCode::W)),
))), Binding::from_single(InputType::from_scan_code(ScanCode(37))),
) ]),
.with(
MyAction::Forward,
Binding::new(ButtonCombination::Single(InputType::Keyboard(
Keyboard::ScanCode(ScanCode(37)),
))),
) )
.with( .with(
MyAction::Right, MyAction::Right,
Binding::new(ButtonCombination::Single(InputType::Keyboard( BindingConfig::from_vec(vec![
Keyboard::KeyCode(KeyCode::D), Binding::from_single(InputType::from_key_code(KeyCode::D)),
))), Binding::from_single(InputType::from_scan_code(ScanCode(38))),
) ]),
.with(
MyAction::Right,
Binding::new(ButtonCombination::Single(InputType::Keyboard(
Keyboard::ScanCode(ScanCode(38)),
))),
) )
.with( .with(
MyAction::Up, MyAction::Up,
Binding::new(ButtonCombination::Single(InputType::Keyboard( BindingConfig::from_bind(Binding::from_single(InputType::from_key_code(
Keyboard::KeyCode(KeyCode::Space), KeyCode::Space,
))), ))),
) )
.with( .with(
MyAction::Down, MyAction::Down,
Binding::new(ButtonCombination::Chord(vec![ BindingConfig::from_bind(Binding::from_chord(vec![
InputType::Keyboard(Keyboard::KeyCode(KeyCode::ShiftLeft)), InputType::from_key_code(KeyCode::ShiftLeft),
InputType::Keyboard(Keyboard::KeyCode(KeyCode::Space)), InputType::from_key_code(KeyCode::Space),
])), ])),
) )
.build(), .build(),
+65 -13
View File
@@ -207,7 +207,18 @@ common_traits_conditions! {
// TODO: Gamepad(GamepadButtonType), // TODO: Gamepad(GamepadButtonType),
// TODO: Touch screen // TODO: Touch screen
} }
}
impl InputType {
pub fn from_key_code(key_code: KeyCode) -> Self {
InputType::Keyboard(Keyboard::KeyCode(key_code))
}
pub fn from_scan_code(scan_code: ScanCode) -> Self {
InputType::Keyboard(Keyboard::ScanCode(scan_code))
}
}
common_traits_conditions! {
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum Keyboard { pub enum Keyboard {
KeyCode(KeyCode), KeyCode(KeyCode),
@@ -231,16 +242,19 @@ common_traits_conditions! {
} }
/// Represents a binding that can be changed by the player /// Represents a binding that can be changed by the player
#[derive(Debug, PartialEq, Clone)] /// default: [`Immutable`](OptionsMode::Immutable)
#[derive(Debug, PartialEq, Clone, Default)]
pub enum OptionsMode { pub enum OptionsMode {
/// Represents a binding that cannot be changed as it is crucial to the game logic /// Represents a binding that cannot be changed as it is crucial to the game logic
#[default] // I think this is more safe
Immutable, Immutable,
/// Represents a binding that the player can customize /// Represents a binding that the player can customize
Customizable, Customizable,
} }
/// Represents the manner in which input is registered /// Represents the manner in which input is registered
#[derive(Debug, PartialEq, Clone)] /// default: [`Tap`](ActivationMode::Tap)
#[derive(Debug, PartialEq, Clone, Default)]
pub enum ActivationMode { pub enum ActivationMode {
/// for<'a> Action<'a> is triggered when the button is pressed /// for<'a> Action<'a> is triggered when the button is pressed
Hold, Hold,
@@ -248,6 +262,7 @@ common_traits_conditions! {
/// and cannot be triggered again until the button is released /// and cannot be triggered again until the button is released
/// you don't need to release all buttons in chord /// you don't need to release all buttons in chord
/// you can release only last pressed button /// you can release only last pressed button
#[default] // I think this is more safe
Tap, Tap,
} }
@@ -265,8 +280,8 @@ common_traits_conditions! {
impl Default for ActivationOptions { impl Default for ActivationOptions {
fn default() -> Self { fn default() -> Self {
Self { Self {
mode: ActivationMode::Tap, // Because it is more safe mode: ActivationMode::default(),
option: OptionsMode::Immutable, option: OptionsMode::default(),
delay: 0.05, // 20 times per second; 14 - world record? delay: 0.05, // 20 times per second; 14 - world record?
} }
} }
@@ -331,6 +346,20 @@ impl<Gs: GameState> Binding<Gs> {
} }
} }
pub fn from_single(input: InputType) -> Self {
Self {
input: ButtonCombination::Single(input),
conditions: Vec::new(),
}
}
pub fn from_chord(input: Vec<InputType>) -> Self {
Self {
input: ButtonCombination::Chord(input),
conditions: Vec::new(),
}
}
pub fn with_condition(mut self, condition: BindingCondition<Gs>) -> Self { pub fn with_condition(mut self, condition: BindingCondition<Gs>) -> Self {
self.conditions.push(condition); self.conditions.push(condition);
self self
@@ -415,19 +444,24 @@ impl<Gs: GameState> Default for Bindings<Gs> {
fn default() -> Self { fn default() -> Self {
Self { Self {
list: Vec::new(), list: Vec::new(),
options: OptionsMode::Immutable, options: OptionsMode::default(),
} }
} }
} }
impl<Gs: GameState> Bindings<Gs> { impl<Gs: GameState> Bindings<Gs> {
pub fn new(bindings: Vec<Binding<Gs>>, options: OptionsMode) -> Self { pub fn new(bindings: Vec<Binding<Gs>>) -> Self {
Self { Self {
list: bindings, list: bindings,
options, ..Default::default()
} }
} }
pub fn with_option_mode(mut self, options: OptionsMode) -> Self {
self.options = options;
self
}
pub fn force_push(&mut self, binding: Binding<Gs>) { pub fn force_push(&mut self, binding: Binding<Gs>) {
self.list.push(binding); self.list.push(binding);
} }
@@ -495,12 +529,31 @@ common_traits_conditions! {
} }
impl<Gs: GameState> BindingConfig<Gs> { impl<Gs: GameState> BindingConfig<Gs> {
pub fn new(bindings: Bindings<Gs>, activation: ActivationOptions) -> Self { pub fn new(bindings: Bindings<Gs>) -> Self {
Self { Self {
bindings, bindings,
activation, ..Default::default()
} }
} }
pub fn from_vec(bindings: Vec<Binding<Gs>>) -> Self {
Self {
bindings: Bindings::new(bindings),
..Default::default()
}
}
pub fn from_bind(binding: Binding<Gs>) -> Self {
Self {
bindings: Bindings::new(vec![binding]),
..Default::default()
}
}
pub fn with_activation_options(mut self, activation: ActivationOptions) -> Self {
self.activation = activation;
self
}
} }
#[derive(Deref, DerefMut)] #[derive(Deref, DerefMut)]
@@ -508,8 +561,8 @@ pub struct ControlsBuilder<A: Action, Gs: GameState>(Controls<A, Gs>);
impl<A: Action, Gs: GameState> ControlsBuilder<A, Gs> { impl<A: Action, Gs: GameState> ControlsBuilder<A, Gs> {
/// ///
pub fn with(mut self, action: A, binding: Binding<Gs>) -> Self { pub fn with(mut self, action: A, config: BindingConfig<Gs>) -> Self {
self.force_push(action, binding); self.insert(action, config);
self self
} }
@@ -527,10 +580,9 @@ common_traits_conditions! {
/// Contains all bindings for actions /// Contains all bindings for actions
#[derive(Resource, Clone, Deref, DerefMut)] #[derive(Resource, Clone, Deref, DerefMut)]
#[cfg_attr(feature = "reflect", reflect(Resource))] #[cfg_attr(feature = "reflect", reflect(Resource))]
// TODO: Add delay for input
pub struct Controls<A: Action, Gs: GameState>( pub struct Controls<A: Action, Gs: GameState>(
#[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))] #[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))]
// TODO: change HashMap to Vec for faster iteration // FIXME: change HashMap to Vec for faster iteration
HashMap<A, BindingConfig<Gs>> HashMap<A, BindingConfig<Gs>>
); );
} }