!
This commit is contained in:
@@ -1,12 +1,21 @@
|
|||||||
# v0.1.0
|
# v0.1.0
|
||||||
|
- [ ] core
|
||||||
|
- [x] single keys
|
||||||
|
- [ ] chords
|
||||||
- [x] contracts
|
- [x] contracts
|
||||||
- [x] loby
|
- [x] loby
|
||||||
- [x] actions
|
- [x] actions
|
||||||
- [x] game state
|
- [x] game state
|
||||||
|
- [ ] serialization & deserialization with ignoring imutable values
|
||||||
- [x] way to define default controlls
|
- [x] way to define default controlls
|
||||||
- [ ] layers for controlls: if on (shift + space) action enable, ignore on (space) action
|
- [ ] layers for controlls: if on (shift + space) action enable, ignore on (space) action
|
||||||
- [ ] warnings on actions collision
|
- [ ] warnings on actions collision
|
||||||
- [ ] basic example
|
- [ ] examples
|
||||||
|
- [ ] basic example
|
||||||
|
- [ ] serialization example
|
||||||
|
- [ ] action options work
|
||||||
|
- [ ] deley
|
||||||
|
- [ ] tap / hold
|
||||||
|
|
||||||
# v0.2.0
|
# v0.2.0
|
||||||
- [ ] layers for controlls: user posibility give priority to controls
|
- [ ] layers for controlls: user posibility give priority to controls
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ use bevy_controls::{
|
|||||||
contract::InputsContainer,
|
contract::InputsContainer,
|
||||||
plugin::ControlsPlugin,
|
plugin::ControlsPlugin,
|
||||||
resource::{
|
resource::{
|
||||||
ActivationOptions, Binding, BindingConfig, Bindings, ButtonCombination, Controls, InputType,
|
ActivationMode, ActivationOptions, Binding, BindingCondition, BindingConfig, Bindings,
|
||||||
InputValue, Keyboard, OptionsMode, PlayerInputs,
|
ButtonCombination, Controls, InputType, InputValue, Keyboard, OptionsMode, PlayerInputs,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use bevy_controls_derive::{Action, GameState};
|
use bevy_controls_derive::{Action, GameState};
|
||||||
@@ -31,6 +31,7 @@ enum MyAction {
|
|||||||
enum MyGameState {
|
enum MyGameState {
|
||||||
#[default]
|
#[default]
|
||||||
InGame,
|
InGame,
|
||||||
|
Menu,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource, Default, Clone, Debug)]
|
#[derive(Resource, Default, Clone, Debug)]
|
||||||
@@ -83,8 +84,12 @@ fn main() {
|
|||||||
// `О` for `ru` layout, because it uses `Keyboard::ScanCode`
|
// `О` 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)),
|
||||||
))),
|
)))
|
||||||
])),
|
.with_condition(BindingCondition::InGameState(MyGameState::InGame)),
|
||||||
|
]))
|
||||||
|
.with_activation_options(
|
||||||
|
ActivationOptions::new(ActivationMode::Tap, OptionsMode::Immutable).with_delay(0.05),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
// the rest will be simplified
|
// the rest will be simplified
|
||||||
.with(
|
.with(
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use bevy_app::{App, Plugin, Update};
|
use bevy_app::{App, Plugin, PreUpdate, Update};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
schedule::State,
|
schedule::State,
|
||||||
system::{Res, ResMut},
|
system::{Res, ResMut},
|
||||||
@@ -43,14 +43,13 @@ impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> ControlsPlugin<A, Ic, Gs>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> Plugin for ControlsPlugin<A, Ic, Gs> {
|
impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> Plugin for ControlsPlugin<A, Ic, Gs> {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app
|
app
|
||||||
.insert_resource(self.controls.clone())
|
.insert_resource(self.controls.clone())
|
||||||
.init_resource::<Ic>()
|
.init_resource::<Ic>()
|
||||||
.add_state::<Gs>()
|
.add_state::<Gs>()
|
||||||
.add_systems(Update, Self::save_input);
|
.add_systems(PreUpdate, Self::collect_inputs);
|
||||||
|
|
||||||
#[cfg(feature = "reflect")]
|
#[cfg(feature = "reflect")]
|
||||||
app.register_type::<Controls<A, Gs>>();
|
app.register_type::<Controls<A, Gs>>();
|
||||||
@@ -58,224 +57,83 @@ impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> Plugin for ControlsPlugin
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> ControlsPlugin<A, Ic, Gs> {
|
impl<A: Action, Ic: InputsContainer<A>, Gs: GameState> ControlsPlugin<A, Ic, Gs> {
|
||||||
/// Process all hard inputs and bindings to update [`PlayerInputs`]
|
fn collect_inputs(
|
||||||
fn save_input(
|
|
||||||
keyboard_input_keycode: Res<Input<KeyCode>>,
|
keyboard_input_keycode: Res<Input<KeyCode>>,
|
||||||
keyboard_input_scancode: Res<Input<ScanCode>>,
|
keyboard_input_scancode: Res<Input<ScanCode>>,
|
||||||
mouse_input: Res<Input<MouseButton>>,
|
|
||||||
mut inputs_container: ResMut<Ic>,
|
|
||||||
controls: Res<Controls<A, Gs>>,
|
|
||||||
game_state: Res<State<Gs>>,
|
|
||||||
) {
|
) {
|
||||||
// get current client inputs
|
|
||||||
if let Some(inputs) = inputs_container.me_mut() {
|
|
||||||
for (action, config) in controls.iter() {
|
|
||||||
'bindings_loop: for binding in config.bindings.iter() {
|
|
||||||
// check binding conditions
|
|
||||||
for condition in &binding.conditions {
|
|
||||||
match condition {
|
|
||||||
BindingCondition::InGameState(state) => {
|
|
||||||
if *state != *game_state.get() {
|
|
||||||
continue 'bindings_loop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log::trace!("action binding {:?} {:?} in condition", action, binding);
|
let a: Vec<(InputType, InputValue)> = vec![];
|
||||||
|
for key in keyboard_input_keycode.get_pressed() {
|
||||||
|
|
||||||
// check inputs & mark actions
|
|
||||||
match &binding.input {
|
|
||||||
ButtonCombination::Single(button) => match button {
|
|
||||||
InputType::Keyboard(Keyboard::KeyCode(key)) => {
|
|
||||||
if keyboard_input_keycode.pressed(*key) {
|
|
||||||
inputs.forced_set(*action, true);
|
|
||||||
break; // when on binding trigered no sence check another
|
|
||||||
}
|
|
||||||
inputs.forced_set(*action, false); // FIXME: this is happening on every pass so maybe too frequently
|
|
||||||
}
|
|
||||||
InputType::Keyboard(Keyboard::ScanCode(code)) => {
|
|
||||||
log::trace!("presed?: {:?}", keyboard_input_scancode.pressed(*code));
|
|
||||||
log::trace!("active");
|
|
||||||
if keyboard_input_scancode.pressed(*code) {
|
|
||||||
inputs.forced_set(*action, true);
|
|
||||||
break; // when on binding trigered no sence check another
|
|
||||||
}
|
|
||||||
inputs.forced_set(*action, false); // FIXME: this is happening on every pass so maybe too frequently
|
|
||||||
}
|
|
||||||
InputType::Mouse(input) => match input {
|
|
||||||
MouseInput::Axis(_axis) => {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
MouseInput::Button(button) => {
|
|
||||||
if !mouse_input.get_pressed().any(|b| b == button) {
|
|
||||||
continue 'bindings_loop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MouseInput::Wheel(_axis) => {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ButtonCombination::Chord(buttons) => {
|
|
||||||
// If any button in chord is not pressed we skip this `binding`
|
|
||||||
for button in buttons {
|
|
||||||
match button {
|
|
||||||
InputType::Keyboard(Keyboard::KeyCode(key)) => {
|
|
||||||
if !keyboard_input_keycode.pressed(*key) {
|
|
||||||
continue 'bindings_loop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
InputType::Keyboard(Keyboard::ScanCode(code)) => {
|
|
||||||
if !keyboard_input_scancode.pressed(*code) {
|
|
||||||
continue 'bindings_loop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
InputType::Mouse(input) => match input {
|
|
||||||
MouseInput::Axis(_axis) => {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
MouseInput::Button(button) => {
|
|
||||||
if !mouse_input.get_pressed().any(|b| b == button) {
|
|
||||||
continue 'bindings_loop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MouseInput::Wheel(_axis) => {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log::trace!("active");
|
|
||||||
// TODO: should be [`Chord`](ButtonCombination::Chord) only [`Boolean`](InputValue::Boolean) type
|
|
||||||
inputs.forced_set(*action, true);
|
|
||||||
break; // when on binding trigered no sence check another
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log::error!("cannot find me in inputs container")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fill_players_inputs() {}
|
||||||
|
|
||||||
|
// /// Process all hard inputs and bindings to update [`PlayerInputs`]
|
||||||
|
// fn save_input(
|
||||||
|
// keyboard_input_keycode: Res<Input<KeyCode>>,
|
||||||
|
// keyboard_input_scancode: Res<Input<ScanCode>>,
|
||||||
|
// mouse_input: Res<Input<MouseButton>>,
|
||||||
|
// mut inputs_container: ResMut<Ic>,
|
||||||
|
// controls: Res<Controls<A, Gs>>,
|
||||||
|
// game_state: Res<State<Gs>>,
|
||||||
|
// ) {
|
||||||
|
// // get current client inputs
|
||||||
|
// if let Some(inputs) = inputs_container.me_mut() {
|
||||||
|
// for (action, config) in controls.iter() {
|
||||||
|
// 'bindings_loop: for binding in config.bindings.iter() {
|
||||||
|
// // check binding conditions
|
||||||
|
// for condition in &binding.conditions {
|
||||||
|
// match condition {
|
||||||
|
// BindingCondition::InGameState(state) => {
|
||||||
|
// if *state != *game_state.get() {
|
||||||
|
// continue 'bindings_loop;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// log::trace!("action binding {:?} {:?} in condition", action, binding);
|
||||||
|
//
|
||||||
|
// // check inputs & mark actions
|
||||||
|
// match &binding.input {
|
||||||
|
// ButtonCombination::Single(button) => match button {
|
||||||
|
// InputType::Keyboard(Keyboard::KeyCode(key)) => {
|
||||||
|
// if keyboard_input_keycode.pressed(*key) {
|
||||||
|
// inputs.forced_set(*action, true);
|
||||||
|
// break; // when on binding trigered no sence check another
|
||||||
|
// }
|
||||||
|
// inputs.forced_set(*action, false); // FIXME: this is happening on every pass so maybe too frequently
|
||||||
|
// }
|
||||||
|
// InputType::Keyboard(Keyboard::ScanCode(code)) => {
|
||||||
|
// log::trace!("presed?: {:?}", keyboard_input_scancode.pressed(*code));
|
||||||
|
// log::trace!("active");
|
||||||
|
// if keyboard_input_scancode.pressed(*code) {
|
||||||
|
// inputs.forced_set(*action, true);
|
||||||
|
// break; // when on binding trigered no sence check another
|
||||||
|
// }
|
||||||
|
// inputs.forced_set(*action, false); // FIXME: this is happening on every pass so maybe too frequently
|
||||||
|
// }
|
||||||
|
// InputType::Mouse(input) => match input {
|
||||||
|
// MouseInput::Axis(_axis) => {
|
||||||
|
// todo!();
|
||||||
|
// }
|
||||||
|
// MouseInput::Button(button) => {
|
||||||
|
// if !mouse_input.get_pressed().any(|b| b == button) {
|
||||||
|
// continue 'bindings_loop;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// MouseInput::Wheel(_axis) => {
|
||||||
|
// todo!();
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// log::error!("cannot find me in inputs container")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[cfg(test)]
|
|
||||||
// mod performance_test {
|
|
||||||
// use crate::{ util::test::{enable_loggings, measure_time, Times}, resource::PlayerInputs};
|
|
||||||
|
|
||||||
// use super::Controls;
|
|
||||||
// use std::time::Duration;
|
|
||||||
|
|
||||||
// /// Test for execution time for [`Controls`] get
|
|
||||||
// ///
|
|
||||||
// /// Example:
|
|
||||||
// /// ```
|
|
||||||
// /// cargo test --package pih-pah-app --lib --features "dev, ui_egui" -- controls::test::controls_get --exact --nocapture
|
|
||||||
// /// ```
|
|
||||||
// #[test]
|
|
||||||
// fn controls_get() {
|
|
||||||
// enable_loggings();
|
|
||||||
|
|
||||||
// let controls = Controls::default();
|
|
||||||
|
|
||||||
// let duration = measure_time(
|
|
||||||
// || {
|
|
||||||
// controls.get(Action::LeverEditorForward);
|
|
||||||
// controls.get(Action::LevelEditorBackward);
|
|
||||||
// controls.get(Action::LevelEditorLeft);
|
|
||||||
// controls.get(Action::LevelEditorRight);
|
|
||||||
// },
|
|
||||||
// Times::default(),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// log::info!("time: {:?}", duration);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// Test for execution speed for [`PlayerInputs`] get
|
|
||||||
// fn player_inputs_get() -> Duration {
|
|
||||||
// enable_loggings();
|
|
||||||
|
|
||||||
// let inputs = PlayerInputs::default();
|
|
||||||
|
|
||||||
// let duration = measure_time(
|
|
||||||
// || {
|
|
||||||
// inputs.get(Action::LeverEditorForward);
|
|
||||||
// inputs.get(Action::LevelEditorBackward);
|
|
||||||
// inputs.get(Action::LevelEditorLeft);
|
|
||||||
// inputs.get(Action::LevelEditorRight);
|
|
||||||
// },
|
|
||||||
// Times::default(),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// duration
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// Test for execution speed for [`PlayerInputs`] get_many
|
|
||||||
// fn player_inputs_get_many() -> Duration {
|
|
||||||
// enable_loggings();
|
|
||||||
|
|
||||||
// let inputs = PlayerInputs::default();
|
|
||||||
|
|
||||||
// let duration = measure_time(
|
|
||||||
// || {
|
|
||||||
// inputs.get_many(vec![
|
|
||||||
// Action::LeverEditorForward,
|
|
||||||
// Action::LevelEditorBackward,
|
|
||||||
// Action::LevelEditorLeft,
|
|
||||||
// Action::LevelEditorRight,
|
|
||||||
// ]);
|
|
||||||
// },
|
|
||||||
// Times::default(),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// duration
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// Test for execution speed for [`PlayerInputs`] get and get_many
|
|
||||||
// ///
|
|
||||||
// /// Example:
|
|
||||||
// /// ```
|
|
||||||
// /// cargo test --package pih-pah-app --lib --features "dev, ui_egui" -- controls::test::compare_player_inputs_get_and_get_many --exact --nocapture
|
|
||||||
// /// ```
|
|
||||||
// #[test]
|
|
||||||
// fn compare_player_inputs_get_and_get_many() {
|
|
||||||
// let get = player_inputs_get();
|
|
||||||
// let get_many = player_inputs_get_many();
|
|
||||||
|
|
||||||
// log::info!("get: {:?}", get);
|
|
||||||
// log::info!("get_many: {:?}", get_many);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// Test for execution speed for [`InputValue`] casting
|
|
||||||
// #[test]
|
|
||||||
// fn action_casting() {
|
|
||||||
// enable_loggings();
|
|
||||||
|
|
||||||
// let inputs = PlayerInputs::default();
|
|
||||||
|
|
||||||
// let duration = measure_time(
|
|
||||||
// || {
|
|
||||||
// let _ = (inputs.get(Action::LeverEditorForward).as_boolean() as i8
|
|
||||||
// - inputs.get(Action::LevelEditorBackward).as_boolean() as i8)
|
|
||||||
// as f32;
|
|
||||||
// },
|
|
||||||
// 10000000.into(),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// log::info!("with casting: {:?}", duration);
|
|
||||||
|
|
||||||
// let inputs = PlayerInputs::default();
|
|
||||||
|
|
||||||
// let duration = measure_time(
|
|
||||||
// || {
|
|
||||||
// let _ = inputs.get(Action::LeverEditorForward);
|
|
||||||
// let _ = inputs.get(Action::LevelEditorBackward);
|
|
||||||
// },
|
|
||||||
// 10000000.into(),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// log::info!("without casting: {:?}", duration);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -47,27 +47,27 @@ where
|
|||||||
common_traits_conditions! {
|
common_traits_conditions! {
|
||||||
/// Struct that contains user's inputs corresponding to actions
|
/// Struct that contains user's inputs corresponding to actions
|
||||||
#[derive(Debug, PartialEq, Clone, Deref, DerefMut)]
|
#[derive(Debug, PartialEq, Clone, Deref, DerefMut)]
|
||||||
pub struct Inputs<A: Action>(#[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))] HashMap<A, InputValue>);
|
pub struct Actions<A: Action>(#[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))] HashMap<A, InputValue>);
|
||||||
|
|
||||||
/// Resource that contains current user inputs
|
/// Resource that contains current user inputs
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct PlayerInputs<A: Action> {
|
pub struct PlayerActions<A: Action> {
|
||||||
#[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))]
|
#[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))]
|
||||||
current: Inputs<A>,
|
current: Actions<A>,
|
||||||
#[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))]
|
#[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))]
|
||||||
previous: Inputs<A>,
|
previous: Actions<A>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Action> Default for PlayerInputs<A> {
|
impl<A: Action> Default for PlayerActions<A> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
PlayerInputs {
|
PlayerActions {
|
||||||
current: Inputs(
|
current: Actions(
|
||||||
A::iter()
|
A::iter()
|
||||||
.map(|action| (action, InputValue::Empty))
|
.map(|action| (action, InputValue::Empty))
|
||||||
.collect(),
|
.collect(),
|
||||||
),
|
),
|
||||||
previous: Inputs(
|
previous: Actions(
|
||||||
A::iter()
|
A::iter()
|
||||||
.map(|action| (action, InputValue::Empty))
|
.map(|action| (action, InputValue::Empty))
|
||||||
.collect(),
|
.collect(),
|
||||||
@@ -76,7 +76,7 @@ impl<A: Action> Default for PlayerInputs<A> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Action> PlayerInputs<A> {
|
impl<A: Action> PlayerActions<A> {
|
||||||
/// Returns current input value for action
|
/// Returns current input value for action
|
||||||
///
|
///
|
||||||
/// in any case faster that `get_many` method
|
/// in any case faster that `get_many` method
|
||||||
@@ -171,7 +171,7 @@ impl<A: Action> PlayerInputs<A> {
|
|||||||
/// Update current inputs
|
/// Update current inputs
|
||||||
///
|
///
|
||||||
/// notice: not recomended to use if you do not know what do you do
|
/// notice: not recomended to use if you do not know what do you do
|
||||||
pub fn forced_update(&mut self, inputs: Inputs<A>) {
|
pub fn forced_update(&mut self, inputs: Actions<A>) {
|
||||||
*self.previous = self.current.clone().0;
|
*self.previous = self.current.clone().0;
|
||||||
*self.current = inputs.0;
|
*self.current = inputs.0;
|
||||||
}
|
}
|
||||||
@@ -181,7 +181,7 @@ impl<A: Action> PlayerInputs<A> {
|
|||||||
/// ! remember that it work safely but slow
|
/// ! remember that it work safely but slow
|
||||||
///
|
///
|
||||||
/// notice: not recomended to use if you do not know what do you do
|
/// notice: not recomended to use if you do not know what do you do
|
||||||
pub fn update(&mut self, inputs: Inputs<A>) -> Result<(), Box<dyn error::Error>> {
|
pub fn update(&mut self, inputs: Actions<A>) -> Result<(), Box<dyn error::Error>> {
|
||||||
// SAFETY: action is always valid
|
// SAFETY: action is always valid
|
||||||
// because we iterate over all actions in `default` method
|
// because we iterate over all actions in `default` method
|
||||||
for (action, value) in inputs.0.iter() {
|
for (action, value) in inputs.0.iter() {
|
||||||
@@ -308,8 +308,8 @@ common_traits_conditions! {
|
|||||||
pub enum ButtonCombination {
|
pub enum ButtonCombination {
|
||||||
/// Single button press
|
/// Single button press
|
||||||
Single(InputType),
|
Single(InputType),
|
||||||
/// Chord is a combination of buttons that must be pressed at the same time
|
// /// Chord is a combination of buttons that must be pressed at the same time
|
||||||
Chord(Vec<InputType>),
|
// Chord(Vec<InputType>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
@@ -353,12 +353,12 @@ impl<Gs: GameState> Binding<Gs> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_chord(input: Vec<InputType>) -> Self {
|
// pub fn from_chord(input: Vec<InputType>) -> Self {
|
||||||
Self {
|
// Self {
|
||||||
input: ButtonCombination::Chord(input),
|
// input: ButtonCombination::Chord(input),
|
||||||
conditions: Vec::new(),
|
// 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);
|
||||||
|
|||||||
Reference in New Issue
Block a user