├── .gitignore ├── src ├── lib.rs ├── input_id.rs ├── common.rs ├── plugin.rs ├── config.rs ├── system.rs └── user_input.rs ├── rustfmt.toml ├── Cargo.toml ├── LICENSE ├── Readme.md └── examples └── common.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /Cargo.lock 2 | /target -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod common; 2 | pub mod config; 3 | pub mod input_id; 4 | pub mod plugin; 5 | pub mod system; 6 | pub mod user_input; 7 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # Bevy Advanced Input Plugin rustfmt configuration 2 | 3 | # Force 2018 edition, shadowing Cargo.toml 4 | edition = "2018" 5 | 6 | # Force 4 space tabs 7 | tab_spaces = 4 8 | 9 | # Prevent carriage returns 10 | newline_style = "Unix" 11 | -------------------------------------------------------------------------------- /src/input_id.rs: -------------------------------------------------------------------------------- 1 | static mut NEXT_INPUT_INDEX: u8 = 0; 2 | 3 | #[derive(Debug, PartialEq, Eq)] 4 | pub struct InputId { 5 | pub id: u8, 6 | } 7 | 8 | impl InputId { 9 | pub fn new(id: u8) -> Self { 10 | Self { id } 11 | } 12 | } 13 | 14 | impl Default for InputId { 15 | fn default() -> Self { 16 | unsafe { 17 | let component = Self::new(NEXT_INPUT_INDEX); 18 | NEXT_INPUT_INDEX += 1; 19 | component 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/common.rs: -------------------------------------------------------------------------------- 1 | use std::hash::Hash; 2 | 3 | use bevy::utils::HashMap; 4 | 5 | pub trait InsertOrGet { 6 | fn insert_or_get(&mut self, item: K) -> &mut V; 7 | } 8 | 9 | impl InsertOrGet for HashMap { 10 | fn insert_or_get(&mut self, item: K) -> &mut V { 11 | return match self.entry(item) { 12 | std::collections::hash_map::Entry::Occupied(o) => o.into_mut(), 13 | std::collections::hash_map::Entry::Vacant(v) => v.insert(V::default()), 14 | }; 15 | } 16 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bevy_advanced_input" 3 | version = "0.2.0" 4 | authors = ["sadpython"] 5 | edition = "2018" 6 | exclude = [".vscode/**"] 7 | description = "Advanced axis and keyset input in Bevy" 8 | license = "MIT" 9 | repository = "https://github.com/sadpython/bevy_advanced_input" 10 | readme = "README.md" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | bevy = { version = "0.5.0", features = ["serialize"], default-features = false } 16 | serde = { version = "1.0.125", features = ["derive"] } 17 | serde_json = { version = "1.0.64"} 18 | 19 | [[example]] 20 | name = "common" 21 | path = "examples/common.rs" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Andrey Vinnik 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/plugin.rs: -------------------------------------------------------------------------------- 1 | use std::hash::Hash; 2 | use std::marker::PhantomData; 3 | 4 | use super::{system::input_system, user_input::UserInputHandle}; 5 | use bevy::prelude::Plugin; 6 | use bevy::prelude::{IntoSystem, ParallelSystemDescriptorCoercion}; 7 | pub struct InputBindingPlugin 8 | where 9 | InputType: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 10 | KeyType: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 11 | { 12 | phantom: PhantomData, 13 | phantom2: PhantomData, 14 | } 15 | 16 | impl Default for InputBindingPlugin 17 | where 18 | InputType: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 19 | KeyType: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 20 | { 21 | fn default() -> Self { 22 | Self { 23 | phantom: PhantomData, 24 | phantom2: PhantomData, 25 | } 26 | } 27 | } 28 | 29 | impl Plugin for InputBindingPlugin 30 | where 31 | InputType: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 32 | KeyType: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 33 | { 34 | fn build(&self, app: &mut bevy::prelude::AppBuilder) { 35 | app.init_resource::>() 36 | .add_system( 37 | input_system:: 38 | .system() 39 | .label("raw_input"), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | use bevy::{prelude::KeyCode, utils::HashMap}; 2 | use std::hash::Hash; 3 | 4 | use crate::common::InsertOrGet; 5 | use crate::user_input::InputAxisType; 6 | use serde::{Deserialize, Serialize}; 7 | 8 | #[derive(Serialize, Deserialize, Clone)] 9 | pub struct InputConfig 10 | where 11 | BindingType: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 12 | { 13 | pub(crate) convert_pressed_key_to: HashMap, 14 | axis_multiplyer: HashMap>, 15 | common_axis_multiplyer: HashMap, 16 | } 17 | 18 | impl InputConfig 19 | where 20 | BindingType: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 21 | { 22 | pub fn new() -> Self { 23 | Self { 24 | convert_pressed_key_to: HashMap::default(), 25 | axis_multiplyer: HashMap::default(), 26 | common_axis_multiplyer: HashMap::default(), 27 | } 28 | } 29 | } 30 | 31 | impl Default for InputConfig 32 | where 33 | BindingType: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 34 | { 35 | fn default() -> Self { 36 | Self::new() 37 | } 38 | } 39 | 40 | impl InputConfig 41 | where 42 | BindingType: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 43 | { 44 | pub fn rebind_axis(&mut self, from: InputAxisType, to: InputAxisType) { 45 | self.convert_pressed_key_to.insert(from, to); 46 | } 47 | 48 | pub fn rebind_default_value(&mut self, input_axis: InputAxisType, modifier: f32) { 49 | self.common_axis_multiplyer.insert(input_axis, modifier); 50 | } 51 | 52 | pub fn get_default_value(&self, input_axis: &InputAxisType) -> f32 { 53 | if let Some(value) = self.common_axis_multiplyer.get(&input_axis) { 54 | return *value; 55 | } 56 | 1.0 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/system.rs: -------------------------------------------------------------------------------- 1 | use std::hash::Hash; 2 | 3 | use bevy::{ 4 | input::{ 5 | keyboard::KeyboardInput, 6 | mouse::{MouseButtonInput, MouseMotion, MouseWheel}, 7 | }, 8 | math::Vec2, 9 | prelude::{EventReader, GamepadEvent, ResMut}, 10 | window::CursorMoved, 11 | }; 12 | 13 | use super::user_input::UserInputHandle; 14 | 15 | //TODO: add touch support with gestures 16 | #[allow(clippy::too_many_arguments)] 17 | pub(crate) fn input_system( 18 | mut evr_keys: EventReader, 19 | 20 | mut evr_cursor: EventReader, 21 | 22 | mut evr_motion: EventReader, 23 | 24 | mut evr_mousebtn: EventReader, 25 | 26 | mut evr_scroll: EventReader, 27 | 28 | mut evr_gamepad: EventReader, 29 | mut user_input: ResMut>, 30 | ) where 31 | InputType: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 32 | KeyType: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 33 | { 34 | user_input.finish_processing(); 35 | // Keyboard input 36 | for ev in evr_keys.iter() { 37 | let state = ev.state; 38 | if let Some(key_code) = ev.key_code { 39 | user_input.process_keyboard_key(key_code, state); 40 | } 41 | } 42 | 43 | // Absolute cursor position (in window coordinates) 44 | for (abs_position, delta_position) in evr_cursor.iter().zip(evr_motion.iter()) { 45 | user_input.process_mouse(abs_position.position, delta_position.delta); 46 | } 47 | 48 | // Mouse buttons 49 | for ev in evr_mousebtn.iter() { 50 | user_input.process_mouse_button(ev.button, ev.state); 51 | } 52 | 53 | // scrolling (mouse wheel, touchpad, etc.) 54 | for ev in evr_scroll.iter() { 55 | user_input.process_mouse_wheel(Vec2::new(ev.x, ev.y)); 56 | } 57 | 58 | // for ev in evr_touch.iter() { 59 | // user_input.process_touch(ev.id as u8, ev.position, ev.phase); 60 | // } 61 | 62 | //Gamepad input 63 | for ev_gmp in evr_gamepad.iter() { 64 | user_input.process_gamepad(ev_gmp.0, ev_gmp.1.clone()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Bevy advanced inputs 2 | This plugin provide functionality for bindig axises and keysets in bevy. 3 | 4 | ### Create input types and bindings 5 | ```rust 6 | #[derive(PartialEq, Eq, Hash, Clone, Copy)] 7 | enum InputType { 8 | Editor, 9 | MainMenu, 10 | Game, 11 | } 12 | 13 | #[derive(PartialEq, Eq, Hash, Clone, Copy)] 14 | enum Bindings { 15 | Hotkeys(HotkeysInput), 16 | Movement(MovementInput), 17 | Camera(CameraInput), 18 | } 19 | 20 | #[derive(PartialEq, Eq, Hash, Clone, Copy)] 21 | enum MovementInput { 22 | Forward, 23 | Right, 24 | Up, 25 | } 26 | #[derive(PartialEq, Eq, Hash, Clone, Copy)] 27 | enum CameraInput { 28 | Yaw, 29 | Pitch, 30 | } 31 | 32 | #[derive(PartialEq, Eq, Hash, Clone, Copy)] 33 | enum HotkeysInput { 34 | Test, 35 | } 36 | ``` 37 | 38 | ### Binding input 39 | ```rust 40 | let mut config: InputConfig = InputConfig::new(); 41 | config.rebind_default_value(InputAxisType::KeyboardButton(KeyCode::S), -1.0); 42 | config.rebind_default_value(InputAxisType::KeyboardButton(KeyCode::A), -1.0); 43 | config.rebind_default_value(InputAxisType::KeyboardButton(KeyCode::Q), -1.0); 44 | config.rebind_default_value( 45 | InputAxisType::GamepadButton(GamepadButtonType::LeftTrigger2), 46 | -1.0, 47 | ); 48 | 49 | let mut set = UserInputSet::new() 50 | 51 | set.begin_key(Bindings::Hotkeys(HotkeysInput::Test)) 52 | .add(&[ 53 | InputAxisType::KeyboardButton(KeyCode::Q), 54 | InputAxisType::KeyboardButton(KeyCode::W), 55 | ]) 56 | .enable_repeat_all_for_reactivation(); 57 | 58 | set.begin_axis(Bindings::Movement(MovementInput::Forward)) 59 | .add(InputAxisType::KeyboardButton(KeyCode::W)) 60 | .add(InputAxisType::KeyboardButton(KeyCode::S)) 61 | .add( 62 | InputAxisType::GamepadAxis(GamepadAxisType::LeftStickY), 63 | None, 64 | ); 65 | 66 | input_bindings.apply_config(&config); 67 | ``` 68 | 69 | ### Spawn entity with InputID 70 | ```rust 71 | fn spawn_player( 72 | mut commands: Commands, 73 | mut input_bindings: ResMut>, 74 | ) { 75 | commands.spawn().insert_bundle(PlayerBundle { 76 | player: Player {}, 77 | input_id: input_bindings.create_input_id(InputType::Editor), 78 | }); 79 | } 80 | ``` 81 | 82 | ### Handle input 83 | ```rust 84 | fn process_player_input( 85 | input_bindings: Res>, 86 | query: Query<&InputID>, 87 | ) { 88 | query.for_each_mut(|input_component| { 89 | if let Some(input_handle) = input_bindings.to_handle(input_component) { 90 | if let Some(value) = input_handle.get_axis_value(Bindings::Movement(MovementInput::Forward)) 91 | {} 92 | 93 | if let Some(value) = input_handle.get_key_state(Bindings::Hotkeys(HotkeysInput::Test)) 94 | {} 95 | } 96 | }); 97 | } 98 | ``` 99 | 100 | ### Other Functions 101 | Create new input id for handle it from input_bindings 102 | ```rust 103 | input_bindings.create_input_id(InputType::Editor) 104 | ``` 105 | Switch to new input bindings set for InputID 106 | ```rust 107 | input_bindings.switch_input(&component, InputType::Editor); 108 | ``` 109 | Remove unused input from input_bindings 110 | ```rust 111 | input_bindings.stop_input_tracking(&component); 112 | ``` 113 | Take current input source(Mouse, Keyboard, Gamepad). Could be used for game widgets, when you want to add button icon to it 114 | ```rust 115 | input_bindings.get_input_source() 116 | ``` 117 | Take mouse position and delta 118 | ```rust 119 | input_bindings.get_mouse_postion(); 120 | input_bindings.get_mouse_delta(); 121 | ``` 122 | Create input handle for InputID, take current input type for InputID 123 | ```rust 124 | if let Some(input_handle) = input_bindings.to_handle(input_component){ 125 | let current_input_type = input_handle.get_input_type(); 126 | } 127 | ``` 128 | Handle multiple gamepads provided by InputID number. First created InputID has id equal 0, second equal 1. And first connected gamepad also will have id equal 0, second equal 1. InputID limited only by u8 numbers. 129 | 130 | ### Examples 131 | See examples/common.rs for more information 132 | 133 | ### Limitations 134 | Currently, plugin didn't support touch inputs, because posssibly it should have gestures processor and algorythm for simply generate gesture and handle it. 135 | Also, gamepad values now filtered by abs(value) > 0.1 136 | -------------------------------------------------------------------------------- /examples/common.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | 3 | use bevy_advanced_input::{ 4 | config::InputConfig, 5 | input_id::InputId, 6 | plugin::InputBindingPlugin, 7 | user_input::{InputAxisType, MouseAxisType, UserInputHandle, UserInputSet}, 8 | }; 9 | 10 | #[derive(PartialEq, Eq, Hash, Clone, Copy)] //* Step 1: create input states for your game 11 | enum InputType { 12 | Editor, 13 | MainMenu, 14 | Game, 15 | } 16 | 17 | #[derive(PartialEq, Eq, Hash, Clone, Copy)] //* Step 2: At second: create binginds enum for all game inputs: main menu, camera, movement 18 | enum Bindings { 19 | Hotkeys(HotkeysInput), 20 | Movement(MovementInput), 21 | Camera(CameraInput), 22 | } 23 | 24 | #[derive(PartialEq, Eq, Hash, Clone, Copy)] //* Step 2.1: And create enums for all of these situations 25 | enum MovementInput { 26 | Forward, 27 | Right, 28 | Up, 29 | } 30 | #[derive(PartialEq, Eq, Hash, Clone, Copy)] //* Step 2.2: More enums 31 | enum CameraInput { 32 | Yaw, 33 | Pitch, 34 | } 35 | 36 | #[derive(PartialEq, Eq, Hash, Clone, Copy)] //* Step2.3: And even more enums 37 | enum HotkeysInput { 38 | Test, 39 | } 40 | 41 | #[derive(Debug)] 42 | struct Player {} //* Step 3: Just player placeholder, you could skip this 43 | 44 | #[derive(Bundle)] 45 | struct PlayerBundle { 46 | //* Step 3.1: Player bundle also optional, you need only InputComponent on your entity 47 | player: Player, 48 | input_id: InputId, 49 | } 50 | //* Step 3.2: Create player config struct with your bindings 51 | struct MyInputConfig(InputConfig); 52 | 53 | fn main() { 54 | App::build() 55 | .add_plugins(DefaultPlugins) 56 | .add_plugin(InputBindingPlugin::::default()) //* Step 4: Add plugin with your InputType and BindingType 57 | .add_startup_system(setup_input.system().label("setup_inputs")) //* Step 5: Create setup input system 58 | .add_startup_system(spawn_player.system().label("spawn_player")) //* Step 6: Spawn player 59 | .add_system( 60 | process_player_input 61 | .system() 62 | .after("raw_input") 63 | .label("input"), 64 | ) //* Step 7: create process input system, it must be runned after out "raw_input" system 65 | .run(); 66 | } 67 | 68 | fn spawn_player( 69 | mut commands: Commands, 70 | mut input_bindings: ResMut>, 71 | ) { 72 | //* When we spawn our player - use input_bindings.create_input_component(InputType) to create input component 73 | //* It automaticly create InputID with id inside it, technicly it's just a number 74 | commands.spawn().insert_bundle(PlayerBundle { 75 | player: Player {}, 76 | input_id: input_bindings.create_input_id(InputType::Editor), 77 | }); 78 | } 79 | 80 | fn process_player_input( 81 | input_bindings: Res>, 82 | query: Query<&InputId>, 83 | ) { 84 | //* If need to track last input type - use input_bindings.get_input_source() 85 | //* It could be Keyboard, Mouse or Gamepad now, and could be used for game widgets, when you want to add button icon to it 86 | query.for_each_mut(|input_component| { 87 | //* Get input handle 88 | if let Some(input_handle) = input_bindings.to_handle(input_component) { 89 | //* Now we can call input_handle.get_axis_value() or input_handle.get_key_state() for track bindigs, see examples below 90 | //* Also, we can get current InputType from input_handle.get_input_type function, because we can! 91 | //* And finally - we can switch input type for out input component input_bindings.swich_input(component, type) 92 | //* If you need mouse position or delta(last frame) call input_bindings.get_mouse_postion() or input_bindings.get_mouse_delta() 93 | if let Some(value) = 94 | input_handle.get_axis_value(Bindings::Movement(MovementInput::Right)) 95 | {} 96 | if let Some(value) = input_handle.get_axis_value(Bindings::Movement(MovementInput::Up)) 97 | { 98 | } 99 | 100 | if let Some(value) = 101 | input_handle.get_axis_value(Bindings::Movement(MovementInput::Forward)) 102 | { 103 | } 104 | 105 | if let Some(value) = input_handle.get_axis_value(Bindings::Camera(CameraInput::Yaw)) {} 106 | if let Some(value) = input_handle.get_axis_value(Bindings::Camera(CameraInput::Pitch)) { 107 | } 108 | if let Some(value) = input_handle.get_key_state(Bindings::Hotkeys(HotkeysInput::Test)) { 109 | } 110 | } 111 | }); 112 | } 113 | 114 | fn setup_input(mut input_bindings: ResMut>) { 115 | //* If you didn't have a config loader, you could setup it right now 116 | let mut config: InputConfig = InputConfig::new(); 117 | //* Rebind default axis value 118 | config.rebind_default_value(InputAxisType::KeyboardButton(KeyCode::S), -1.0); 119 | config.rebind_default_value(InputAxisType::KeyboardButton(KeyCode::A), -1.0); 120 | config.rebind_default_value(InputAxisType::KeyboardButton(KeyCode::Q), -1.0); 121 | config.rebind_default_value( 122 | InputAxisType::GamepadButton(GamepadButtonType::LeftTrigger2), 123 | -1.0, 124 | ); 125 | 126 | //* Or just swap axises 127 | config.rebind_axis( 128 | InputAxisType::KeyboardButton(KeyCode::Key0), 129 | InputAxisType::KeyboardButton(KeyCode::Key1), 130 | ); 131 | 132 | let mut set = UserInputSet::new(); //* Create InputSet at first 133 | 134 | //* And then bind your bindings enum to keys, you could use from 1 to n keys in keyset 135 | //* Optionally - call enable_repeat_all_for_reactivation(), so you need release all of pressed keys and press it again for toggle binding 136 | //* Keys provide "Pressed", "Released" or None state if not changed last tick 137 | set.begin_key(Bindings::Hotkeys(HotkeysInput::Test)) 138 | .add(&[ 139 | InputAxisType::KeyboardButton(KeyCode::Q), 140 | InputAxisType::KeyboardButton(KeyCode::W), 141 | ]) 142 | .enable_repeat_all_for_reactivation(); 143 | 144 | //* Or bind your bindings enum to axis 145 | set.begin_axis(Bindings::Movement(MovementInput::Forward)) 146 | .add(InputAxisType::KeyboardButton(KeyCode::W)) 147 | .add(InputAxisType::KeyboardButton(KeyCode::S)) 148 | .add(InputAxisType::GamepadAxis(GamepadAxisType::LeftStickY)); 149 | 150 | set.begin_axis(Bindings::Movement(MovementInput::Right)) 151 | .add(InputAxisType::KeyboardButton(KeyCode::A)) 152 | .add(InputAxisType::KeyboardButton(KeyCode::D)) 153 | .add(InputAxisType::GamepadAxis(GamepadAxisType::LeftStickX)); 154 | 155 | set.begin_axis(Bindings::Movement(MovementInput::Up)) 156 | .add(InputAxisType::KeyboardButton(KeyCode::Q)) 157 | .add(InputAxisType::KeyboardButton(KeyCode::E)) 158 | .add(InputAxisType::GamepadButton( 159 | GamepadButtonType::RightTrigger2, 160 | )) 161 | .add(InputAxisType::GamepadButton( 162 | GamepadButtonType::LeftTrigger2, 163 | )); 164 | 165 | set.begin_axis(Bindings::Camera(CameraInput::Yaw)) 166 | .add(InputAxisType::MouseAxisDiff(MouseAxisType::X)) 167 | .add(InputAxisType::GamepadAxis(GamepadAxisType::RightStickX)); 168 | 169 | set.begin_axis(Bindings::Camera(CameraInput::Pitch)) 170 | .add(InputAxisType::MouseAxisDiff(MouseAxisType::Y)) 171 | .add(InputAxisType::GamepadAxis(GamepadAxisType::RightStickY)); 172 | 173 | //* Add your input set to bindigs with specified game InputType 174 | input_bindings.add_input(InputType::Editor, set); 175 | 176 | //* And last step - apply your config. It will be applyed to all of your input sets, and you could apply config many times if you need 177 | //* change game settings, for example. Config didn't cached, so 178 | input_bindings.apply_config(&config); 179 | } 180 | -------------------------------------------------------------------------------- /src/user_input.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cell::Ref, 3 | collections::hash_map::{Entry, Keys}, 4 | hash::Hash, 5 | }; 6 | 7 | use bevy::{ 8 | input::ElementState, 9 | math::Vec2, 10 | prelude::{ 11 | Gamepad, GamepadAxisType, GamepadButtonType, GamepadEventType, KeyCode, MouseButton, 12 | }, 13 | utils::{HashMap, HashSet}, 14 | }; 15 | 16 | use crate::{common::InsertOrGet, config::InputConfig}; 17 | 18 | use super::input_id::InputId; 19 | use serde::{Deserialize, Serialize}; 20 | 21 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] 22 | pub enum InputState { 23 | Released, 24 | ShouldBeActivated, 25 | ShouldBeDeactvated, 26 | Pressed, 27 | } 28 | 29 | impl Default for InputState { 30 | fn default() -> Self { 31 | Self::Released 32 | } 33 | } 34 | 35 | #[derive(Clone, Debug)] 36 | pub struct InputKeyset { 37 | pub(crate) state: InputState, 38 | pub(crate) activated_keys_num: usize, 39 | pub(crate) keys_state: HashMap, 40 | pub(crate) repeat_all_for_activate: bool, 41 | pub(crate) default_keys: Vec, 42 | } 43 | 44 | impl InputKeyset { 45 | pub fn new(keyset: Vec, repeat_all_for_activate: bool) -> Self { 46 | let mut set = Self { 47 | state: InputState::Released, 48 | activated_keys_num: 0, 49 | keys_state: HashMap::default(), 50 | repeat_all_for_activate, 51 | default_keys: keyset, 52 | }; 53 | set.reset_to_default(); 54 | set 55 | } 56 | 57 | pub fn update_key_state(&mut self, key: InputAxisType, new_state: ElementState) { 58 | if let Some(val) = self.keys_state.get_mut(&key) { 59 | if *val != new_state { 60 | *val = new_state; 61 | match new_state { 62 | ElementState::Pressed => { 63 | self.activated_keys_num += 1; 64 | } 65 | ElementState::Released => { 66 | if self.repeat_all_for_activate { 67 | self.activated_keys_num = 0; 68 | } else { 69 | self.activated_keys_num -= 1; 70 | } 71 | } 72 | } 73 | if self.activated_keys_num == self.keys_state.len() { 74 | if self.state != InputState::Pressed { 75 | self.state = InputState::ShouldBeActivated; 76 | } 77 | } else if self.state == InputState::Pressed { 78 | self.state = InputState::ShouldBeDeactvated; 79 | } 80 | } 81 | } 82 | } 83 | 84 | pub(crate) fn update_state(&mut self) { 85 | match self.state { 86 | InputState::Released => {} 87 | InputState::ShouldBeActivated => { 88 | self.state = InputState::Pressed; 89 | } 90 | InputState::ShouldBeDeactvated => { 91 | self.state = InputState::Released; 92 | } 93 | InputState::Pressed => {} 94 | } 95 | } 96 | 97 | pub(crate) fn reset_to_default(&mut self) { 98 | self.keys_state.clear(); 99 | for key in self.default_keys.iter() { 100 | self.keys_state.insert(key.clone(), ElementState::Released); 101 | } 102 | } 103 | 104 | pub(crate) fn apply_rebind(&mut self, rebind: &HashMap) { 105 | for key in self.default_keys.iter() { 106 | if let Some(val) = rebind.get(key) { 107 | let key_state = self.keys_state.remove(key).unwrap(); 108 | self.keys_state.insert(val.clone(), key_state); 109 | } 110 | } 111 | } 112 | } 113 | 114 | impl Default for InputKeyset { 115 | fn default() -> Self { 116 | Self::new(Vec::new(), false) 117 | } 118 | } 119 | 120 | #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug, Deserialize, Serialize)] 121 | pub enum MouseAxisType { 122 | X, 123 | Y, 124 | Wheel, 125 | } 126 | 127 | #[derive(PartialEq, Eq, Hash, Clone, Debug, Deserialize, Serialize)] 128 | #[allow(dead_code)] 129 | pub enum InputAxisType { 130 | KeyboardButton(KeyCode), 131 | MouseButton(MouseButton), 132 | GamepadButton(GamepadButtonType), 133 | MouseAxis(MouseAxisType), 134 | MouseAxisDiff(MouseAxisType), 135 | GamepadAxis(GamepadAxisType), 136 | GamepadAxisDiff(GamepadAxisType), 137 | } 138 | 139 | #[derive(Clone, Debug, Deserialize, Serialize)] 140 | pub struct InputAxisSet { 141 | #[serde(skip_serializing, skip_deserializing)] 142 | pub(crate) state: InputState, 143 | pub(crate) axises: HashMap, 144 | #[serde(skip_serializing, skip_deserializing)] 145 | pub(crate) active_axis_types: Vec, 146 | #[serde(skip_serializing, skip_deserializing)] 147 | pub(crate) out_value: Option, 148 | pub(crate) default_axises: HashMap, 149 | } 150 | 151 | impl InputAxisSet { 152 | pub fn new(axises: HashMap) -> Self { 153 | Self { 154 | state: InputState::Released, 155 | axises: axises.clone(), 156 | active_axis_types: Vec::new(), 157 | out_value: None, 158 | default_axises: axises, 159 | } 160 | } 161 | // #[allow(dead_code)] 162 | // pub fn add_axis(&mut self, axis_type: InputAxisType, axis_value: Option) { 163 | // self.axises.insert(axis_type, axis_value); 164 | // } 165 | 166 | pub fn update_axis_state( 167 | &mut self, 168 | axis_type: InputAxisType, 169 | new_state: ElementState, 170 | value: Option, 171 | ) { 172 | if let Entry::Occupied(entry) = self.axises.entry(axis_type.clone()) { 173 | match new_state { 174 | ElementState::Pressed => { 175 | let mut should_update_value = false; 176 | if self.active_axis_types.iter().any(|elem| *elem == axis_type) { 177 | if *self.active_axis_types.last().unwrap() == axis_type { 178 | should_update_value = true; 179 | } 180 | } else { 181 | self.active_axis_types.push(axis_type); 182 | should_update_value = true; 183 | if self.active_axis_types.len() == 1 { 184 | self.state = InputState::ShouldBeActivated; 185 | } 186 | } 187 | 188 | if should_update_value { 189 | let default_value = entry.get(); 190 | let new_value = value.unwrap_or(1.0); 191 | self.out_value = Some(default_value * new_value); 192 | } 193 | } 194 | ElementState::Released => { 195 | if let Some(index) = self 196 | .active_axis_types 197 | .iter() 198 | .position(|elem| *elem == axis_type) 199 | { 200 | self.active_axis_types.remove(index); 201 | 202 | if let Some(last_axis) = self.active_axis_types.last() { 203 | let axis = last_axis.clone(); 204 | self.update_axis_state(axis, ElementState::Pressed, None); 205 | } else { 206 | self.out_value = None; 207 | self.state = InputState::ShouldBeDeactvated; 208 | } 209 | } 210 | } 211 | } 212 | } 213 | } 214 | 215 | fn get_value(&self) -> Option { 216 | self.out_value 217 | } 218 | 219 | pub(crate) fn update_state(&mut self) { 220 | match self.state { 221 | InputState::Released => {} 222 | InputState::ShouldBeActivated => { 223 | self.state = InputState::Pressed; 224 | } 225 | InputState::ShouldBeDeactvated => { 226 | self.state = InputState::Released; 227 | self.active_axis_types.clear(); 228 | } 229 | InputState::Pressed => {} 230 | } 231 | } 232 | 233 | pub(crate) fn reset_to_default(&mut self) { 234 | self.axises = self.default_axises.clone(); 235 | self.active_axis_types = Vec::new(); 236 | self.state = InputState::Released; 237 | self.out_value = None; 238 | } 239 | 240 | pub(crate) fn apply_rebind(&mut self, rebind: &HashMap) { 241 | for (key, value) in self.default_axises.iter() { 242 | if let Some(val) = rebind.get(key) { 243 | let axis_default_value = self.axises.remove(key).unwrap(); 244 | self.axises.insert(val.clone(), axis_default_value); 245 | } 246 | } 247 | } 248 | 249 | pub(crate) fn get_axises(&self) -> Keys { 250 | self.default_axises.keys() 251 | } 252 | pub(crate) fn apply_new_defaults(&mut self, new_defaults: HashMap) {} 253 | } 254 | #[derive(Clone)] 255 | pub struct UserInputSet 256 | where 257 | Key: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 258 | { 259 | name_to_keyset: HashMap, 260 | name_to_axisset: HashMap, 261 | last_gamepad_axis_value: HashMap, 262 | } 263 | 264 | pub struct AxisSetBuilder<'a, Key> 265 | where 266 | Key: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 267 | { 268 | axises: HashSet, 269 | name: Key, 270 | owner_set: &'a mut UserInputSet, 271 | } 272 | 273 | impl<'a, Key> AxisSetBuilder<'a, Key> 274 | where 275 | Key: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 276 | { 277 | pub fn add(&mut self, axis_type: InputAxisType) -> &mut Self { 278 | self.axises.insert(axis_type); 279 | self 280 | } 281 | 282 | fn finish(&mut self) { 283 | self.owner_set.add_axisset(self.name, self.axises.clone()); 284 | } 285 | } 286 | 287 | impl Drop for AxisSetBuilder<'_, Key> 288 | where 289 | Key: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 290 | { 291 | fn drop(&mut self) { 292 | self.finish(); 293 | } 294 | } 295 | 296 | pub struct KeySetBuilder<'a, Key> 297 | where 298 | Key: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 299 | { 300 | axises: Vec, 301 | name: Key, 302 | owner_set: &'a mut UserInputSet, 303 | repeat_all_for_reactivate: bool, 304 | } 305 | 306 | impl<'a, Key> KeySetBuilder<'a, Key> 307 | where 308 | Key: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 309 | { 310 | pub fn add(&mut self, keys: &[InputAxisType]) -> &mut Self { 311 | let mut vec = keys.to_vec(); 312 | self.axises.append(&mut vec); 313 | self 314 | } 315 | 316 | pub fn enable_repeat_all_for_reactivation(&mut self) -> &mut Self { 317 | self.repeat_all_for_reactivate = true; 318 | self 319 | } 320 | 321 | fn finish(&mut self) { 322 | self.owner_set.add_keyset( 323 | self.name, 324 | self.axises.clone(), 325 | self.repeat_all_for_reactivate, 326 | ); 327 | } 328 | } 329 | 330 | impl Drop for KeySetBuilder<'_, Key> 331 | where 332 | Key: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 333 | { 334 | fn drop(&mut self) { 335 | self.finish(); 336 | } 337 | } 338 | 339 | impl UserInputSet 340 | where 341 | Key: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 342 | { 343 | pub fn new() -> Self { 344 | Self { 345 | name_to_keyset: HashMap::default(), 346 | name_to_axisset: HashMap::default(), 347 | last_gamepad_axis_value: HashMap::default(), 348 | } 349 | } 350 | 351 | pub fn begin_key(&mut self, name: Key) -> KeySetBuilder { 352 | KeySetBuilder { 353 | axises: Vec::new(), 354 | name: name, 355 | owner_set: self, 356 | repeat_all_for_reactivate: false, 357 | } 358 | } 359 | 360 | #[allow(dead_code)] 361 | pub(crate) fn add_keyset( 362 | &mut self, 363 | name: Key, 364 | keyset: Vec, 365 | repeat_all_for_activate: bool, 366 | ) { 367 | self.name_to_keyset 368 | .insert(name, InputKeyset::new(keyset, repeat_all_for_activate)); 369 | } 370 | 371 | #[allow(dead_code)] 372 | pub fn begin_axis(&mut self, name: Key) -> AxisSetBuilder { 373 | AxisSetBuilder { 374 | axises: HashSet::default(), 375 | name: name, 376 | owner_set: self, 377 | } 378 | } 379 | 380 | #[allow(dead_code)] 381 | pub(crate) fn add_axisset(&mut self, name: Key, axises: HashSet) { 382 | let mut map = HashMap::default(); 383 | for axis in axises.iter() { 384 | map.insert(axis.clone(), 1.0); 385 | } 386 | self.name_to_axisset.insert(name, InputAxisSet::new(map)); 387 | } 388 | 389 | pub fn get_axis_value(&self, name: Key) -> Option { 390 | if let Some(val) = self.name_to_axisset.get(&name) { 391 | return val.get_value(); 392 | } 393 | None 394 | } 395 | 396 | #[allow(dead_code)] 397 | pub fn get_key_state(&self, name: Key) -> Option { 398 | if let Some(val) = self.name_to_keyset.get(&name) { 399 | if val.state == InputState::ShouldBeActivated { 400 | return Some(ElementState::Pressed); 401 | } else if val.state == InputState::ShouldBeDeactvated { 402 | return Some(ElementState::Released); 403 | } 404 | } 405 | None 406 | } 407 | 408 | pub(crate) fn change_key_state(&mut self, key_type: InputAxisType, state: ElementState) { 409 | for (_, keyset) in self.name_to_keyset.iter_mut() { 410 | keyset.update_key_state(key_type.clone(), state); 411 | } 412 | } 413 | 414 | pub(crate) fn change_axis_state( 415 | &mut self, 416 | axis_type: InputAxisType, 417 | state: ElementState, 418 | value: Option, 419 | ) { 420 | for (_, keyset) in self.name_to_axisset.iter_mut() { 421 | keyset.update_axis_state(axis_type.clone(), state, value); 422 | } 423 | } 424 | 425 | pub(crate) fn update_states(&mut self) { 426 | for (_, keyset) in self.name_to_keyset.iter_mut() { 427 | keyset.update_state(); 428 | } 429 | for (_, axisset) in self.name_to_axisset.iter_mut() { 430 | axisset.update_state(); 431 | } 432 | } 433 | pub(crate) fn apply_config(&mut self, config: &InputConfig) { 434 | for (_, keyset) in self.name_to_keyset.iter_mut() { 435 | keyset.reset_to_default(); 436 | keyset.apply_rebind(&config.convert_pressed_key_to); 437 | } 438 | for (_, axisset) in self.name_to_axisset.iter_mut() { 439 | axisset.reset_to_default(); 440 | axisset.apply_rebind(&config.convert_pressed_key_to); 441 | 442 | for (key, value) in axisset.axises.iter_mut() { 443 | *value = config.get_default_value(key); 444 | } 445 | } 446 | } 447 | } 448 | 449 | impl Default for UserInputSet 450 | where 451 | Key: Eq + PartialEq + Clone + Copy + Hash + Send + Sync, 452 | { 453 | fn default() -> Self { 454 | Self::new() 455 | } 456 | } 457 | 458 | #[allow(dead_code)] 459 | pub enum TouchState { 460 | Pressed, 461 | Released, 462 | Moved, 463 | } 464 | 465 | #[allow(dead_code)] 466 | #[derive(Debug, Clone, Copy)] 467 | pub enum InputSource { 468 | Keyboard, 469 | Mouse, 470 | Gamepad, 471 | } 472 | 473 | pub struct UserInputHandle 474 | where 475 | InputType: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 476 | BindingType: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 477 | { 478 | mouse_position: Option, 479 | mouse_delta: Option, 480 | mouse_moved_this_tick: bool, 481 | mouse_wheel_moved_this_tick: bool, 482 | input_id_to_inputset: HashMap>, 483 | input_id_to_input_type: HashMap, 484 | available_sets: HashMap>, 485 | last_input_source: Option, 486 | config: InputConfig, 487 | } 488 | 489 | impl Default for UserInputHandle 490 | where 491 | InputType: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 492 | BindingType: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 493 | { 494 | fn default() -> Self { 495 | Self::new() 496 | } 497 | } 498 | 499 | impl UserInputHandle 500 | where 501 | InputType: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 502 | BindingType: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 503 | { 504 | pub fn new() -> Self { 505 | Self { 506 | mouse_position: None, 507 | mouse_delta: None, 508 | mouse_moved_this_tick: false, 509 | mouse_wheel_moved_this_tick: false, 510 | input_id_to_inputset: HashMap::default(), 511 | input_id_to_input_type: HashMap::default(), 512 | available_sets: HashMap::default(), 513 | last_input_source: None, 514 | config: InputConfig::new(), 515 | } 516 | } 517 | pub(crate) fn process_keyboard_key(&mut self, key: KeyCode, new_state: ElementState) { 518 | for (_, player_set) in self.input_id_to_inputset.iter_mut() { 519 | player_set.change_key_state(InputAxisType::KeyboardButton(key), new_state); 520 | player_set.change_axis_state(InputAxisType::KeyboardButton(key), new_state, None); 521 | } 522 | self.last_input_source = Some(InputSource::Keyboard); 523 | } 524 | pub(crate) fn process_mouse(&mut self, current_position: Vec2, delta_position: Vec2) { 525 | for (_, player_set) in self.input_id_to_inputset.iter_mut() { 526 | player_set.change_axis_state( 527 | InputAxisType::MouseAxis(MouseAxisType::X), 528 | ElementState::Pressed, 529 | Some(current_position.x), 530 | ); 531 | player_set.change_axis_state( 532 | InputAxisType::MouseAxis(MouseAxisType::Y), 533 | ElementState::Pressed, 534 | Some(current_position.y), 535 | ); 536 | player_set.change_axis_state( 537 | InputAxisType::MouseAxisDiff(MouseAxisType::X), 538 | ElementState::Pressed, 539 | Some(delta_position.x), 540 | ); 541 | player_set.change_axis_state( 542 | InputAxisType::MouseAxisDiff(MouseAxisType::Y), 543 | ElementState::Pressed, 544 | Some(delta_position.y), 545 | ); 546 | } 547 | 548 | self.mouse_position = Some(current_position); 549 | self.mouse_delta = Some(delta_position); 550 | 551 | self.mouse_moved_this_tick = true; 552 | self.last_input_source = Some(InputSource::Mouse); 553 | } 554 | pub(crate) fn process_mouse_button(&mut self, button: MouseButton, new_state: ElementState) { 555 | for (_, player_set) in self.input_id_to_inputset.iter_mut() { 556 | player_set.change_key_state(InputAxisType::MouseButton(button), new_state); 557 | player_set.change_axis_state(InputAxisType::MouseButton(button), new_state, None); 558 | } 559 | self.last_input_source = Some(InputSource::Mouse); 560 | } 561 | pub(crate) fn process_mouse_wheel(&mut self, delta: Vec2) { 562 | for (_, player_set) in self.input_id_to_inputset.iter_mut() { 563 | player_set.change_key_state( 564 | InputAxisType::MouseAxis(MouseAxisType::Wheel), 565 | ElementState::Pressed, 566 | ); 567 | player_set.change_key_state( 568 | InputAxisType::MouseAxisDiff(MouseAxisType::Wheel), 569 | ElementState::Pressed, 570 | ); 571 | player_set.change_axis_state( 572 | InputAxisType::MouseAxis(MouseAxisType::Wheel), 573 | ElementState::Pressed, 574 | Some(delta.y), 575 | ); 576 | player_set.change_axis_state( 577 | InputAxisType::MouseAxisDiff(MouseAxisType::Wheel), 578 | ElementState::Pressed, 579 | Some(delta.y), 580 | ); 581 | } 582 | self.mouse_wheel_moved_this_tick = true; 583 | self.last_input_source = Some(InputSource::Mouse); 584 | } 585 | 586 | //TODO: rewrite 587 | // #[allow(dead_code)] 588 | // pub(crate) fn process_touch(&mut self, finger: u8, position: Vec2, state: TouchPhase) { 589 | // //let mut last_touches = input_set.last_touch_indexes; 590 | // match state { 591 | // TouchPhase::Started => { 592 | // for (_, player_set) in self.current_set.iter_mut() { 593 | // player_set.change_key_state( 594 | // InputAxisType::TouchFinger(finger), 595 | // ElementState::Pressed, 596 | // ); 597 | // player_set.touch_to_position.insert(finger, position); 598 | // player_set.last_touch_indexes.push(finger); 599 | // } 600 | // } 601 | // TouchPhase::Moved => { 602 | // for (_, player_set) in self.current_set.iter_mut() { 603 | // player_set.change_axis_state( 604 | // InputAxisType::TouchAxis(TouchAxisType::X), 605 | // ElementState::Pressed, 606 | // Some(position.x), 607 | // ); 608 | // player_set.change_axis_state( 609 | // InputAxisType::TouchAxis(TouchAxisType::Y), 610 | // ElementState::Pressed, 611 | // Some(position.y), 612 | // ); 613 | // let position_diff = match player_set.touch_to_position.entry(finger) { 614 | // Entry::Occupied(mut exists) => { 615 | // let last_position = exists.insert(position); 616 | // position - last_position 617 | // } 618 | // Entry::Vacant(empty) => { 619 | // empty.insert(position); 620 | // Vec2::new(0.0, 0.0) 621 | // } 622 | // }; 623 | // player_set.change_axis_state( 624 | // InputAxisType::TouchAxisDiff(TouchAxisType::X), 625 | // ElementState::Pressed, 626 | // Some(position_diff.x), 627 | // ); 628 | // player_set.change_axis_state( 629 | // InputAxisType::TouchAxisDiff(TouchAxisType::Y), 630 | // ElementState::Pressed, 631 | // Some(position_diff.y), 632 | // ); 633 | // } 634 | // } 635 | // TouchPhase::Ended => { 636 | // for (_, player_set) in self.current_set.iter_mut() { 637 | // player_set.change_key_state( 638 | // InputAxisType::TouchFinger(finger), 639 | // ElementState::Released, 640 | // ); 641 | // player_set.touch_to_position.remove(&finger); 642 | // let index = player_set 643 | // .last_touch_indexes 644 | // .iter() 645 | // .position(|elem| *elem == finger) 646 | // .unwrap(); 647 | // player_set.last_touch_indexes.remove(index); 648 | // } 649 | // } 650 | // TouchPhase::Cancelled => { 651 | // for (_, player_set) in self.current_set.iter_mut() { 652 | // player_set.change_key_state( 653 | // InputAxisType::TouchFinger(finger), 654 | // ElementState::Released, 655 | // ); 656 | // player_set.touch_to_position.remove(&finger); 657 | // let index = player_set 658 | // .last_touch_indexes 659 | // .iter() 660 | // .position(|elem| *elem == finger) 661 | // .unwrap(); 662 | // player_set.last_touch_indexes.remove(index); 663 | // } 664 | // } 665 | // } 666 | // } 667 | 668 | #[allow(dead_code)] 669 | pub(crate) fn process_gamepad(&mut self, gamepad: Gamepad, event: GamepadEventType) { 670 | //TODO: Write connection logic 671 | match event { 672 | GamepadEventType::Connected => {} 673 | GamepadEventType::Disconnected => {} 674 | GamepadEventType::ButtonChanged(btn_type, value) => { 675 | let state = if value.abs() <= 0.1 { 676 | ElementState::Released 677 | } else { 678 | ElementState::Pressed 679 | }; 680 | for (player_id, player_set) in self.input_id_to_inputset.iter_mut() { 681 | if *player_id == gamepad.0 as u8 { 682 | player_set.change_key_state(InputAxisType::GamepadButton(btn_type), state); 683 | player_set.change_axis_state( 684 | InputAxisType::GamepadButton(btn_type), 685 | state, 686 | Some(value), 687 | ); 688 | break; 689 | } 690 | } 691 | self.last_input_source = Some(InputSource::Gamepad); 692 | } 693 | GamepadEventType::AxisChanged(axis_type, value) => { 694 | let state = if value.abs() <= 0.1 { 695 | ElementState::Released 696 | } else { 697 | ElementState::Pressed 698 | }; 699 | for (player_id, player_set) in self.input_id_to_inputset.iter_mut() { 700 | if *player_id == gamepad.0 as u8 { 701 | player_set.change_key_state(InputAxisType::GamepadAxis(axis_type), state); 702 | player_set.change_axis_state( 703 | InputAxisType::GamepadAxis(axis_type), 704 | state, 705 | Some(value), 706 | ); 707 | let diff = match player_set.last_gamepad_axis_value.entry(axis_type) { 708 | Entry::Occupied(mut exists) => { 709 | let last_value = exists.insert(value); 710 | value - last_value 711 | } 712 | Entry::Vacant(empty) => { 713 | empty.insert(value); 714 | 0.0 715 | } 716 | }; 717 | player_set.change_axis_state( 718 | InputAxisType::GamepadAxisDiff(axis_type), 719 | state, 720 | Some(diff), 721 | ); 722 | break; 723 | } 724 | } 725 | 726 | self.last_input_source = Some(InputSource::Gamepad); 727 | } 728 | } 729 | } 730 | 731 | #[allow(dead_code)] 732 | pub fn switch_input(&mut self, component: &'_ InputId, input_type: InputType) { 733 | let should_change_input = 734 | if let Some(exists_input) = self.input_id_to_input_type.get(&component.id) { 735 | *exists_input == input_type 736 | } else { 737 | true 738 | }; 739 | if should_change_input { 740 | if let Entry::Occupied(set) = self.available_sets.entry(input_type) { 741 | self.input_id_to_inputset 742 | .insert(component.id, set.get().clone()); 743 | self.input_id_to_input_type.insert(component.id, input_type); 744 | } 745 | } 746 | } 747 | 748 | #[allow(dead_code)] 749 | pub fn add_input(&mut self, input_type: InputType, input_set: UserInputSet) { 750 | let map = self.available_sets.insert_or_get(input_type); 751 | *map = input_set; 752 | } 753 | 754 | fn update_states(&mut self) { 755 | for (_, player_set) in self.input_id_to_inputset.iter_mut() { 756 | player_set.update_states(); 757 | } 758 | } 759 | 760 | pub(crate) fn finish_processing(&mut self) { 761 | self.update_states(); 762 | for (_, player_set) in self.input_id_to_inputset.iter_mut() { 763 | if !self.mouse_moved_this_tick { 764 | player_set.change_axis_state( 765 | InputAxisType::MouseAxis(MouseAxisType::X), 766 | ElementState::Released, 767 | None, 768 | ); 769 | player_set.change_axis_state( 770 | InputAxisType::MouseAxis(MouseAxisType::Y), 771 | ElementState::Released, 772 | None, 773 | ); 774 | player_set.change_axis_state( 775 | InputAxisType::MouseAxisDiff(MouseAxisType::X), 776 | ElementState::Released, 777 | None, 778 | ); 779 | player_set.change_axis_state( 780 | InputAxisType::MouseAxisDiff(MouseAxisType::Y), 781 | ElementState::Released, 782 | None, 783 | ); 784 | }; 785 | if !self.mouse_wheel_moved_this_tick { 786 | player_set.change_axis_state( 787 | InputAxisType::MouseAxis(MouseAxisType::Wheel), 788 | ElementState::Released, 789 | None, 790 | ); 791 | player_set.change_axis_state( 792 | InputAxisType::MouseAxisDiff(MouseAxisType::Wheel), 793 | ElementState::Released, 794 | None, 795 | ); 796 | player_set.change_key_state( 797 | InputAxisType::MouseAxis(MouseAxisType::Wheel), 798 | ElementState::Released, 799 | ); 800 | player_set.change_key_state( 801 | InputAxisType::MouseAxisDiff(MouseAxisType::Wheel), 802 | ElementState::Released, 803 | ); 804 | }; 805 | } 806 | 807 | self.mouse_moved_this_tick = false; 808 | 809 | self.mouse_wheel_moved_this_tick = false; 810 | 811 | self.mouse_delta = None; 812 | } 813 | 814 | pub fn to_handle( 815 | &self, 816 | component: &'_ InputId, 817 | ) -> Option> { 818 | if let (Some(input_set), Some(input_type)) = ( 819 | self.input_id_to_inputset.get(&component.id), 820 | self.input_id_to_input_type.get(&component.id), 821 | ) { 822 | return Some(InputHandle { 823 | input_set, 824 | input_type, 825 | }); 826 | } 827 | None 828 | } 829 | 830 | pub fn create_input_id(&mut self, input_type: InputType) -> InputId { 831 | let component = InputId::default(); 832 | self.switch_input(&component, input_type); 833 | component 834 | } 835 | 836 | #[allow(dead_code)] 837 | pub fn stop_input_tracking(&mut self, component: &'_ InputId) { 838 | self.input_id_to_inputset.remove(&component.id); 839 | self.input_id_to_input_type.remove(&component.id); 840 | } 841 | 842 | #[allow(dead_code)] 843 | pub fn get_input_source(&self) -> Option { 844 | self.last_input_source 845 | } 846 | 847 | #[allow(dead_code)] 848 | pub fn get_mouse_postion(&self) -> Option { 849 | self.mouse_position 850 | } 851 | 852 | #[allow(dead_code)] 853 | pub fn get_mouse_delta(&self) -> Option { 854 | self.mouse_delta 855 | } 856 | 857 | pub fn apply_config(&mut self, config: &InputConfig) { 858 | for (_, set) in self.available_sets.iter_mut() { 859 | set.apply_config(config); 860 | } 861 | } 862 | } 863 | 864 | pub struct InputHandle<'a, BindingType, InputType> 865 | where 866 | BindingType: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 867 | InputType: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 868 | { 869 | input_set: &'a UserInputSet, 870 | input_type: &'a InputType, 871 | } 872 | 873 | impl InputHandle<'_, BindingType, InputType> 874 | where 875 | BindingType: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 876 | InputType: PartialEq + Eq + Hash + Copy + Clone + Send + Sync, 877 | { 878 | #[allow(dead_code)] 879 | pub fn get_axis_value(&self, name: BindingType) -> Option { 880 | self.input_set.get_axis_value(name) 881 | } 882 | 883 | #[allow(dead_code)] 884 | pub fn get_key_state(&self, name: BindingType) -> Option { 885 | self.input_set.get_key_state(name) 886 | } 887 | 888 | #[allow(dead_code)] 889 | pub fn get_input_type(&self) -> &'_ InputType { 890 | self.input_type 891 | } 892 | } 893 | --------------------------------------------------------------------------------