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::{
contract::InputsContainer,
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 strum_macros::EnumIter;
@@ -68,68 +71,54 @@ fn main() {
}),
ControlsPlugin::<MyAction, MyInputsContainer, MyGameState>::new(
Controls::<MyAction, MyGameState>::new()
// the first control instance I will describe in detail
.with(
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(
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
// `О` for `ru` layout
// `О` for `ru` layout, because it uses `Keyboard::ScanCode`
Binding::new(ButtonCombination::Single(InputType::Keyboard(
Keyboard::ScanCode(ScanCode(35)),
))),
])),
)
// the rest will be simplified
.with(
MyAction::Back,
Binding::new(ButtonCombination::Single(InputType::Keyboard(
Keyboard::KeyCode(KeyCode::S),
))),
)
.with(
MyAction::Back,
Binding::new(ButtonCombination::Single(InputType::Keyboard(
Keyboard::ScanCode(ScanCode(36)),
))),
BindingConfig::from_vec(vec![
Binding::from_single(InputType::from_key_code(KeyCode::S)),
Binding::from_single(InputType::from_scan_code(ScanCode(36))),
]),
)
.with(
MyAction::Forward,
Binding::new(ButtonCombination::Single(InputType::Keyboard(
Keyboard::KeyCode(KeyCode::W),
))),
)
.with(
MyAction::Forward,
Binding::new(ButtonCombination::Single(InputType::Keyboard(
Keyboard::ScanCode(ScanCode(37)),
))),
BindingConfig::from_vec(vec![
Binding::from_single(InputType::from_key_code(KeyCode::W)),
Binding::from_single(InputType::from_scan_code(ScanCode(37))),
]),
)
.with(
MyAction::Right,
Binding::new(ButtonCombination::Single(InputType::Keyboard(
Keyboard::KeyCode(KeyCode::D),
))),
)
.with(
MyAction::Right,
Binding::new(ButtonCombination::Single(InputType::Keyboard(
Keyboard::ScanCode(ScanCode(38)),
))),
BindingConfig::from_vec(vec![
Binding::from_single(InputType::from_key_code(KeyCode::D)),
Binding::from_single(InputType::from_scan_code(ScanCode(38))),
]),
)
.with(
MyAction::Up,
Binding::new(ButtonCombination::Single(InputType::Keyboard(
Keyboard::KeyCode(KeyCode::Space),
BindingConfig::from_bind(Binding::from_single(InputType::from_key_code(
KeyCode::Space,
))),
)
.with(
MyAction::Down,
Binding::new(ButtonCombination::Chord(vec![
InputType::Keyboard(Keyboard::KeyCode(KeyCode::ShiftLeft)),
InputType::Keyboard(Keyboard::KeyCode(KeyCode::Space)),
BindingConfig::from_bind(Binding::from_chord(vec![
InputType::from_key_code(KeyCode::ShiftLeft),
InputType::from_key_code(KeyCode::Space),
])),
)
.build(),
+66 -14
View File
@@ -207,7 +207,18 @@ common_traits_conditions! {
// TODO: Gamepad(GamepadButtonType),
// 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)]
pub enum Keyboard {
KeyCode(KeyCode),
@@ -231,16 +242,19 @@ common_traits_conditions! {
}
/// 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 {
/// Represents a binding that cannot be changed as it is crucial to the game logic
#[default] // I think this is more safe
Immutable,
/// Represents a binding that the player can customize
Customizable,
}
/// Represents the manner in which input is registered
#[derive(Debug, PartialEq, Clone)]
/// default: [`Tap`](ActivationMode::Tap)
#[derive(Debug, PartialEq, Clone, Default)]
pub enum ActivationMode {
/// for<'a> Action<'a> is triggered when the button is pressed
Hold,
@@ -248,6 +262,7 @@ common_traits_conditions! {
/// and cannot be triggered again until the button is released
/// you don't need to release all buttons in chord
/// you can release only last pressed button
#[default] // I think this is more safe
Tap,
}
@@ -265,8 +280,8 @@ common_traits_conditions! {
impl Default for ActivationOptions {
fn default() -> Self {
Self {
mode: ActivationMode::Tap, // Because it is more safe
option: OptionsMode::Immutable,
mode: ActivationMode::default(),
option: OptionsMode::default(),
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 {
self.conditions.push(condition);
self
@@ -415,19 +444,24 @@ impl<Gs: GameState> Default for Bindings<Gs> {
fn default() -> Self {
Self {
list: Vec::new(),
options: OptionsMode::Immutable,
options: OptionsMode::default(),
}
}
}
impl<Gs: GameState> Bindings<Gs> {
pub fn new(bindings: Vec<Binding<Gs>>, options: OptionsMode) -> Self {
pub fn new(bindings: Vec<Binding<Gs>>) -> Self {
Self {
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>) {
self.list.push(binding);
}
@@ -495,21 +529,40 @@ common_traits_conditions! {
}
impl<Gs: GameState> BindingConfig<Gs> {
pub fn new(bindings: Bindings<Gs>, activation: ActivationOptions) -> Self {
pub fn new(bindings: Bindings<Gs>) -> Self {
Self {
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)]
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 {
self.force_push(action, binding);
pub fn with(mut self, action: A, config: BindingConfig<Gs>) -> Self {
self.insert(action, config);
self
}
@@ -527,10 +580,9 @@ common_traits_conditions! {
/// Contains all bindings for actions
#[derive(Resource, Clone, Deref, DerefMut)]
#[cfg_attr(feature = "reflect", reflect(Resource))]
// TODO: Add delay for input
pub struct Controls<A: Action, Gs: GameState>(
#[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>>
);
}