diff --git a/crate/bevy_controls/example/basic/src/main.rs b/crate/bevy_controls/example/basic/src/main.rs index 5cc107e..e7df44a 100644 --- a/crate/bevy_controls/example/basic/src/main.rs +++ b/crate/bevy_controls/example/basic/src/main.rs @@ -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::::new( Controls::::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` - 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 - Binding::new(ButtonCombination::Single(InputType::Keyboard( - Keyboard::ScanCode(ScanCode(35)), - ))), + 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), + ))), + // this way input will work only on any layout on same button, 35 is `H` for `us` layout or + // `О` 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(), diff --git a/crate/bevy_controls/src/resource.rs b/crate/bevy_controls/src/resource.rs index 4abf146..e948f00 100644 --- a/crate/bevy_controls/src/resource.rs +++ b/crate/bevy_controls/src/resource.rs @@ -200,14 +200,25 @@ impl PlayerInputs { } common_traits_conditions! { - #[derive(Debug, PartialEq, Clone)] - pub enum InputType { - Keyboard(Keyboard), - Mouse(MouseInput), - // TODO: Gamepad(GamepadButtonType), - // TODO: Touch screen - } + #[derive(Debug, PartialEq, Clone)] + pub enum InputType { + Keyboard(Keyboard), + Mouse(MouseInput), + // 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 Binding { } } + pub fn from_single(input: InputType) -> Self { + Self { + input: ButtonCombination::Single(input), + conditions: Vec::new(), + } + } + + pub fn from_chord(input: Vec) -> Self { + Self { + input: ButtonCombination::Chord(input), + conditions: Vec::new(), + } + } + pub fn with_condition(mut self, condition: BindingCondition) -> Self { self.conditions.push(condition); self @@ -415,19 +444,24 @@ impl Default for Bindings { fn default() -> Self { Self { list: Vec::new(), - options: OptionsMode::Immutable, + options: OptionsMode::default(), } } } impl Bindings { - pub fn new(bindings: Vec>, options: OptionsMode) -> Self { + pub fn new(bindings: Vec>) -> 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) { self.list.push(binding); } @@ -495,25 +529,44 @@ common_traits_conditions! { } impl BindingConfig { - pub fn new(bindings: Bindings, activation: ActivationOptions) -> Self { + pub fn new(bindings: Bindings) -> Self { Self { bindings, - activation, + ..Default::default() } } + + pub fn from_vec(bindings: Vec>) -> Self { + Self { + bindings: Bindings::new(bindings), + ..Default::default() + } + } + + pub fn from_bind(binding: Binding) -> 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(Controls); -impl ControlsBuilder{ - /// - pub fn with(mut self, action: A, binding: Binding) -> Self { - self.force_push(action, binding); - self +impl ControlsBuilder { + /// + pub fn with(mut self, action: A, config: BindingConfig) -> Self { + self.insert(action, config); + self } - /// + /// pub fn build(mut self) -> Controls { // If you see this error, you may add new action in menu_actions // or make sure that you have only one Menufor<'a> Action<'a> with the same name in the Menufor<'a> Action<'a>s @@ -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( #[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))] - // TODO: change HashMap to Vec for faster iteration + // FIXME: change HashMap to Vec for faster iteration HashMap> ); }