feat: possibility define BindConfig in plugin setup
This commit is contained in:
@@ -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![
|
||||||
Binding::new(ButtonCombination::Single(InputType::Keyboard(
|
// this way input will work only on latin layout only on `A`, because it uses `Keyboard::KeyCode`
|
||||||
Keyboard::KeyCode(KeyCode::A),
|
Binding::new(ButtonCombination::Single(InputType::Keyboard(
|
||||||
))),
|
Keyboard::KeyCode(KeyCode::A),
|
||||||
)
|
))),
|
||||||
.with(
|
// this way input will work only on any layout on same button, 35 is `H` for `us` layout or
|
||||||
MyAction::Left,
|
// `О` for `ru` layout, because it uses `Keyboard::ScanCode`
|
||||||
// this way input will work only on any layout on same button, 35 is `H` for `us` layout or
|
Binding::new(ButtonCombination::Single(InputType::Keyboard(
|
||||||
// `О` for `ru` layout
|
Keyboard::ScanCode(ScanCode(35)),
|
||||||
Binding::new(ButtonCombination::Single(InputType::Keyboard(
|
))),
|
||||||
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(),
|
||||||
|
|||||||
@@ -200,14 +200,25 @@ impl<A: Action> PlayerInputs<A> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
common_traits_conditions! {
|
common_traits_conditions! {
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum InputType {
|
pub enum InputType {
|
||||||
Keyboard(Keyboard),
|
Keyboard(Keyboard),
|
||||||
Mouse(MouseInput),
|
Mouse(MouseInput),
|
||||||
// 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,21 +529,40 @@ 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)]
|
||||||
pub struct ControlsBuilder<A: Action, Gs: GameState>(Controls<A, Gs>);
|
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>>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user