1use core::{ops::RangeInclusive, time::Duration};
4
5use crate::{Axis, ButtonInput, ButtonState};
6use alloc::string::String;
7#[cfg(feature = "bevy_reflect")]
8use bevy_ecs::prelude::ReflectComponent;
9use bevy_ecs::{
10 change_detection::DetectChangesMut,
11 component::Component,
12 entity::Entity,
13 event::{Event, EventReader, EventWriter},
14 name::Name,
15 system::{Commands, Query},
16};
17use bevy_math::ops;
18use bevy_math::Vec2;
19use bevy_platform::collections::HashMap;
20#[cfg(feature = "bevy_reflect")]
21use bevy_reflect::{std_traits::ReflectDefault, Reflect};
22#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
23use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
24use derive_more::derive::From;
25use log::{info, warn};
26use thiserror::Error;
27
28#[derive(Event, Debug, Clone, PartialEq, From)]
36#[cfg_attr(
37 feature = "bevy_reflect",
38 derive(Reflect),
39 reflect(Debug, PartialEq, Clone)
40)]
41#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
42#[cfg_attr(
43 all(feature = "serialize", feature = "bevy_reflect"),
44 reflect(Serialize, Deserialize)
45)]
46pub enum GamepadEvent {
47 Connection(GamepadConnectionEvent),
49 Button(GamepadButtonChangedEvent),
51 Axis(GamepadAxisChangedEvent),
53}
54
55#[derive(Event, Debug, Clone, PartialEq, From)]
63#[cfg_attr(
64 feature = "bevy_reflect",
65 derive(Reflect),
66 reflect(Debug, PartialEq, Clone)
67)]
68#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
69#[cfg_attr(
70 all(feature = "serialize", feature = "bevy_reflect"),
71 reflect(Serialize, Deserialize)
72)]
73pub enum RawGamepadEvent {
74 Connection(GamepadConnectionEvent),
76 Button(RawGamepadButtonChangedEvent),
78 Axis(RawGamepadAxisChangedEvent),
80}
81
82#[derive(Event, Debug, Copy, Clone, PartialEq)]
84#[cfg_attr(
85 feature = "bevy_reflect",
86 derive(Reflect),
87 reflect(Debug, PartialEq, Clone)
88)]
89#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
90#[cfg_attr(
91 all(feature = "serialize", feature = "bevy_reflect"),
92 reflect(Serialize, Deserialize)
93)]
94pub struct RawGamepadButtonChangedEvent {
95 pub gamepad: Entity,
97 pub button: GamepadButton,
99 pub value: f32,
101}
102
103impl RawGamepadButtonChangedEvent {
104 pub fn new(gamepad: Entity, button_type: GamepadButton, value: f32) -> Self {
106 Self {
107 gamepad,
108 button: button_type,
109 value,
110 }
111 }
112}
113
114#[derive(Event, Debug, Copy, Clone, PartialEq)]
116#[cfg_attr(
117 feature = "bevy_reflect",
118 derive(Reflect),
119 reflect(Debug, PartialEq, Clone)
120)]
121#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
122#[cfg_attr(
123 all(feature = "serialize", feature = "bevy_reflect"),
124 reflect(Serialize, Deserialize)
125)]
126pub struct RawGamepadAxisChangedEvent {
127 pub gamepad: Entity,
129 pub axis: GamepadAxis,
131 pub value: f32,
133}
134
135impl RawGamepadAxisChangedEvent {
136 pub fn new(gamepad: Entity, axis_type: GamepadAxis, value: f32) -> Self {
138 Self {
139 gamepad,
140 axis: axis_type,
141 value,
142 }
143 }
144}
145
146#[derive(Event, Debug, Clone, PartialEq)]
149#[cfg_attr(
150 feature = "bevy_reflect",
151 derive(Reflect),
152 reflect(Debug, PartialEq, Clone)
153)]
154#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
155#[cfg_attr(
156 all(feature = "serialize", feature = "bevy_reflect"),
157 reflect(Serialize, Deserialize)
158)]
159pub struct GamepadConnectionEvent {
160 pub gamepad: Entity,
162 pub connection: GamepadConnection,
164}
165
166impl GamepadConnectionEvent {
167 pub fn new(gamepad: Entity, connection: GamepadConnection) -> Self {
169 Self {
170 gamepad,
171 connection,
172 }
173 }
174
175 pub fn connected(&self) -> bool {
177 matches!(self.connection, GamepadConnection::Connected { .. })
178 }
179
180 pub fn disconnected(&self) -> bool {
182 !self.connected()
183 }
184}
185
186#[derive(Event, Debug, Clone, Copy, PartialEq, Eq)]
188#[cfg_attr(
189 feature = "bevy_reflect",
190 derive(Reflect),
191 reflect(Debug, PartialEq, Clone)
192)]
193#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
194#[cfg_attr(
195 all(feature = "serialize", feature = "bevy_reflect"),
196 reflect(Serialize, Deserialize)
197)]
198pub struct GamepadButtonStateChangedEvent {
199 pub entity: Entity,
201 pub button: GamepadButton,
203 pub state: ButtonState,
205}
206
207impl GamepadButtonStateChangedEvent {
208 pub fn new(entity: Entity, button: GamepadButton, state: ButtonState) -> Self {
210 Self {
211 entity,
212 button,
213 state,
214 }
215 }
216}
217
218#[derive(Event, Debug, Clone, Copy, PartialEq)]
220#[cfg_attr(
221 feature = "bevy_reflect",
222 derive(Reflect),
223 reflect(Debug, PartialEq, Clone)
224)]
225#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
226#[cfg_attr(
227 all(feature = "serialize", feature = "bevy_reflect"),
228 reflect(Serialize, Deserialize)
229)]
230pub struct GamepadButtonChangedEvent {
231 pub entity: Entity,
233 pub button: GamepadButton,
235 pub state: ButtonState,
237 pub value: f32,
239}
240
241impl GamepadButtonChangedEvent {
242 pub fn new(entity: Entity, button: GamepadButton, state: ButtonState, value: f32) -> Self {
244 Self {
245 entity,
246 button,
247 state,
248 value,
249 }
250 }
251}
252
253#[derive(Event, Debug, Clone, Copy, PartialEq)]
255#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
256#[cfg_attr(
257 feature = "bevy_reflect",
258 derive(Reflect),
259 reflect(Debug, PartialEq, Clone)
260)]
261#[cfg_attr(
262 all(feature = "bevy_reflect", feature = "serialize"),
263 reflect(Serialize, Deserialize)
264)]
265pub struct GamepadAxisChangedEvent {
266 pub entity: Entity,
268 pub axis: GamepadAxis,
270 pub value: f32,
272}
273
274impl GamepadAxisChangedEvent {
275 pub fn new(entity: Entity, axis: GamepadAxis, value: f32) -> Self {
277 Self {
278 entity,
279 axis,
280 value,
281 }
282 }
283}
284
285#[derive(Error, Debug, PartialEq)]
287pub enum AxisSettingsError {
288 #[error("invalid livezone_lowerbound {0}, expected value [-1.0..=0.0]")]
290 LiveZoneLowerBoundOutOfRange(f32),
291 #[error("invalid deadzone_lowerbound {0}, expected value [-1.0..=0.0]")]
293 DeadZoneLowerBoundOutOfRange(f32),
294 #[error("invalid deadzone_upperbound {0}, expected value [0.0..=1.0]")]
296 DeadZoneUpperBoundOutOfRange(f32),
297 #[error("invalid livezone_upperbound {0}, expected value [0.0..=1.0]")]
299 LiveZoneUpperBoundOutOfRange(f32),
300 #[error("invalid parameter values livezone_lowerbound {} deadzone_lowerbound {}, expected livezone_lowerbound <= deadzone_lowerbound", livezone_lowerbound, deadzone_lowerbound)]
302 LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
303 livezone_lowerbound: f32,
305 deadzone_lowerbound: f32,
307 },
308 #[error("invalid parameter values livezone_upperbound {} deadzone_upperbound {}, expected deadzone_upperbound <= livezone_upperbound", livezone_upperbound, deadzone_upperbound)]
310 DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
311 livezone_upperbound: f32,
313 deadzone_upperbound: f32,
315 },
316 #[error("invalid threshold {0}, expected 0.0 <= threshold <= 2.0")]
318 Threshold(f32),
319}
320
321#[derive(Error, Debug, PartialEq)]
323pub enum ButtonSettingsError {
324 #[error("invalid release_threshold {0}, expected value [0.0..=1.0]")]
326 ReleaseThresholdOutOfRange(f32),
327 #[error("invalid press_threshold {0}, expected [0.0..=1.0]")]
329 PressThresholdOutOfRange(f32),
330 #[error("invalid parameter values release_threshold {} press_threshold {}, expected release_threshold <= press_threshold", release_threshold, press_threshold)]
332 ReleaseThresholdGreaterThanPressThreshold {
333 press_threshold: f32,
335 release_threshold: f32,
337 },
338}
339
340#[derive(Component, Debug)]
369#[cfg_attr(
370 feature = "bevy_reflect",
371 derive(Reflect),
372 reflect(Debug, Component, Default)
373)]
374#[require(GamepadSettings)]
375pub struct Gamepad {
376 pub(crate) vendor_id: Option<u16>,
378
379 pub(crate) product_id: Option<u16>,
381
382 pub(crate) digital: ButtonInput<GamepadButton>,
384
385 pub(crate) analog: Axis<GamepadInput>,
387}
388
389impl Gamepad {
390 pub fn vendor_id(&self) -> Option<u16> {
392 self.vendor_id
393 }
394
395 pub fn product_id(&self) -> Option<u16> {
399 self.product_id
400 }
401
402 pub fn get(&self, input: impl Into<GamepadInput>) -> Option<f32> {
406 self.analog.get(input.into())
407 }
408
409 pub fn get_unclamped(&self, input: impl Into<GamepadInput>) -> Option<f32> {
413 self.analog.get_unclamped(input.into())
414 }
415
416 pub fn left_stick(&self) -> Vec2 {
418 Vec2 {
419 x: self.get(GamepadAxis::LeftStickX).unwrap_or(0.0),
420 y: self.get(GamepadAxis::LeftStickY).unwrap_or(0.0),
421 }
422 }
423
424 pub fn right_stick(&self) -> Vec2 {
426 Vec2 {
427 x: self.get(GamepadAxis::RightStickX).unwrap_or(0.0),
428 y: self.get(GamepadAxis::RightStickY).unwrap_or(0.0),
429 }
430 }
431
432 pub fn dpad(&self) -> Vec2 {
434 Vec2 {
435 x: self.get(GamepadButton::DPadRight).unwrap_or(0.0)
436 - self.get(GamepadButton::DPadLeft).unwrap_or(0.0),
437 y: self.get(GamepadButton::DPadUp).unwrap_or(0.0)
438 - self.get(GamepadButton::DPadDown).unwrap_or(0.0),
439 }
440 }
441
442 pub fn pressed(&self, button_type: GamepadButton) -> bool {
444 self.digital.pressed(button_type)
445 }
446
447 pub fn any_pressed(&self, button_inputs: impl IntoIterator<Item = GamepadButton>) -> bool {
449 self.digital.any_pressed(button_inputs)
450 }
451
452 pub fn all_pressed(&self, button_inputs: impl IntoIterator<Item = GamepadButton>) -> bool {
454 self.digital.all_pressed(button_inputs)
455 }
456
457 pub fn just_pressed(&self, button_type: GamepadButton) -> bool {
461 self.digital.just_pressed(button_type)
462 }
463
464 pub fn any_just_pressed(&self, button_inputs: impl IntoIterator<Item = GamepadButton>) -> bool {
466 self.digital.any_just_pressed(button_inputs)
467 }
468
469 pub fn all_just_pressed(&self, button_inputs: impl IntoIterator<Item = GamepadButton>) -> bool {
471 self.digital.all_just_pressed(button_inputs)
472 }
473
474 pub fn just_released(&self, button_type: GamepadButton) -> bool {
478 self.digital.just_released(button_type)
479 }
480
481 pub fn any_just_released(
483 &self,
484 button_inputs: impl IntoIterator<Item = GamepadButton>,
485 ) -> bool {
486 self.digital.any_just_released(button_inputs)
487 }
488
489 pub fn all_just_released(
491 &self,
492 button_inputs: impl IntoIterator<Item = GamepadButton>,
493 ) -> bool {
494 self.digital.all_just_released(button_inputs)
495 }
496
497 pub fn get_pressed(&self) -> impl Iterator<Item = &GamepadButton> {
501 self.digital.get_pressed()
502 }
503
504 pub fn get_just_pressed(&self) -> impl Iterator<Item = &GamepadButton> {
508 self.digital.get_just_pressed()
509 }
510
511 pub fn get_just_released(&self) -> impl Iterator<Item = &GamepadButton> {
515 self.digital.get_just_released()
516 }
517
518 pub fn get_analog_axes(&self) -> impl Iterator<Item = &GamepadInput> {
520 self.analog.all_axes()
521 }
522
523 pub fn digital(&self) -> &ButtonInput<GamepadButton> {
525 &self.digital
526 }
527
528 pub fn digital_mut(&mut self) -> &mut ButtonInput<GamepadButton> {
530 &mut self.digital
531 }
532
533 pub fn analog(&self) -> &Axis<GamepadInput> {
535 &self.analog
536 }
537
538 pub fn analog_mut(&mut self) -> &mut Axis<GamepadInput> {
540 &mut self.analog
541 }
542}
543
544impl Default for Gamepad {
545 fn default() -> Self {
546 let mut analog = Axis::default();
547 for button in GamepadButton::all().iter().copied() {
548 analog.set(button, 0.0);
549 }
550 for axis_type in GamepadAxis::all().iter().copied() {
551 analog.set(axis_type, 0.0);
552 }
553
554 Self {
555 vendor_id: None,
556 product_id: None,
557 digital: Default::default(),
558 analog,
559 }
560 }
561}
562
563#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
570#[cfg_attr(
571 feature = "bevy_reflect",
572 derive(Reflect),
573 reflect(Debug, Hash, PartialEq, Clone)
574)]
575#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
576#[cfg_attr(
577 all(feature = "serialize", feature = "bevy_reflect"),
578 reflect(Serialize, Deserialize)
579)]
580pub enum GamepadButton {
581 South,
583 East,
585 North,
587 West,
589
590 C,
592 Z,
594
595 LeftTrigger,
597 LeftTrigger2,
599 RightTrigger,
601 RightTrigger2,
603 Select,
605 Start,
607 Mode,
609
610 LeftThumb,
612 RightThumb,
614
615 DPadUp,
617 DPadDown,
619 DPadLeft,
621 DPadRight,
623
624 Other(u8),
626}
627
628impl GamepadButton {
629 pub const fn all() -> [GamepadButton; 19] {
631 [
632 GamepadButton::South,
633 GamepadButton::East,
634 GamepadButton::North,
635 GamepadButton::West,
636 GamepadButton::C,
637 GamepadButton::Z,
638 GamepadButton::LeftTrigger,
639 GamepadButton::LeftTrigger2,
640 GamepadButton::RightTrigger,
641 GamepadButton::RightTrigger2,
642 GamepadButton::Select,
643 GamepadButton::Start,
644 GamepadButton::Mode,
645 GamepadButton::LeftThumb,
646 GamepadButton::RightThumb,
647 GamepadButton::DPadUp,
648 GamepadButton::DPadDown,
649 GamepadButton::DPadLeft,
650 GamepadButton::DPadRight,
651 ]
652 }
653}
654
655#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
662#[cfg_attr(
663 feature = "bevy_reflect",
664 derive(Reflect),
665 reflect(Debug, PartialEq, Hash, Clone)
666)]
667#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
668#[cfg_attr(
669 all(feature = "serialize", feature = "bevy_reflect"),
670 reflect(Serialize, Deserialize)
671)]
672pub enum GamepadAxis {
673 LeftStickX,
675 LeftStickY,
677 LeftZ,
680 RightStickX,
682 RightStickY,
684 RightZ,
687 Other(u8),
689}
690
691impl GamepadAxis {
692 pub const fn all() -> [GamepadAxis; 6] {
694 [
695 GamepadAxis::LeftStickX,
696 GamepadAxis::LeftStickY,
697 GamepadAxis::LeftZ,
698 GamepadAxis::RightStickX,
699 GamepadAxis::RightStickY,
700 GamepadAxis::RightZ,
701 ]
702 }
703}
704
705#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, From)]
708#[cfg_attr(
709 feature = "bevy_reflect",
710 derive(Reflect),
711 reflect(Debug, Hash, PartialEq, Clone)
712)]
713pub enum GamepadInput {
714 Axis(GamepadAxis),
716 Button(GamepadButton),
718}
719
720#[derive(Component, Clone, Default, Debug)]
734#[cfg_attr(
735 feature = "bevy_reflect",
736 derive(Reflect),
737 reflect(Debug, Default, Component, Clone)
738)]
739pub struct GamepadSettings {
740 pub default_button_settings: ButtonSettings,
742 pub default_axis_settings: AxisSettings,
744 pub default_button_axis_settings: ButtonAxisSettings,
746 pub button_settings: HashMap<GamepadButton, ButtonSettings>,
748 pub axis_settings: HashMap<GamepadAxis, AxisSettings>,
750 pub button_axis_settings: HashMap<GamepadButton, ButtonAxisSettings>,
752}
753
754impl GamepadSettings {
755 pub fn get_button_settings(&self, button: GamepadButton) -> &ButtonSettings {
768 self.button_settings
769 .get(&button)
770 .unwrap_or(&self.default_button_settings)
771 }
772
773 pub fn get_axis_settings(&self, axis: GamepadAxis) -> &AxisSettings {
786 self.axis_settings
787 .get(&axis)
788 .unwrap_or(&self.default_axis_settings)
789 }
790
791 pub fn get_button_axis_settings(&self, button: GamepadButton) -> &ButtonAxisSettings {
804 self.button_axis_settings
805 .get(&button)
806 .unwrap_or(&self.default_button_axis_settings)
807 }
808}
809
810#[derive(Debug, PartialEq, Clone)]
818#[cfg_attr(
819 feature = "bevy_reflect",
820 derive(Reflect),
821 reflect(Debug, Default, Clone)
822)]
823pub struct ButtonSettings {
824 press_threshold: f32,
825 release_threshold: f32,
826}
827
828impl Default for ButtonSettings {
829 fn default() -> Self {
830 ButtonSettings {
831 press_threshold: 0.75,
832 release_threshold: 0.65,
833 }
834 }
835}
836
837impl ButtonSettings {
838 pub fn new(
855 press_threshold: f32,
856 release_threshold: f32,
857 ) -> Result<ButtonSettings, ButtonSettingsError> {
858 if !(0.0..=1.0).contains(&release_threshold) {
859 Err(ButtonSettingsError::ReleaseThresholdOutOfRange(
860 release_threshold,
861 ))
862 } else if !(0.0..=1.0).contains(&press_threshold) {
863 Err(ButtonSettingsError::PressThresholdOutOfRange(
864 press_threshold,
865 ))
866 } else if release_threshold > press_threshold {
867 Err(
868 ButtonSettingsError::ReleaseThresholdGreaterThanPressThreshold {
869 press_threshold,
870 release_threshold,
871 },
872 )
873 } else {
874 Ok(ButtonSettings {
875 press_threshold,
876 release_threshold,
877 })
878 }
879 }
880
881 pub fn is_pressed(&self, value: f32) -> bool {
885 value >= self.press_threshold
886 }
887
888 pub fn is_released(&self, value: f32) -> bool {
892 value <= self.release_threshold
893 }
894
895 pub fn press_threshold(&self) -> f32 {
897 self.press_threshold
898 }
899
900 pub fn try_set_press_threshold(&mut self, value: f32) -> Result<(), ButtonSettingsError> {
908 if (self.release_threshold..=1.0).contains(&value) {
909 self.press_threshold = value;
910 Ok(())
911 } else if !(0.0..1.0).contains(&value) {
912 Err(ButtonSettingsError::PressThresholdOutOfRange(value))
913 } else {
914 Err(
915 ButtonSettingsError::ReleaseThresholdGreaterThanPressThreshold {
916 press_threshold: value,
917 release_threshold: self.release_threshold,
918 },
919 )
920 }
921 }
922
923 pub fn set_press_threshold(&mut self, value: f32) -> f32 {
928 self.try_set_press_threshold(value).ok();
929 self.press_threshold
930 }
931
932 pub fn release_threshold(&self) -> f32 {
934 self.release_threshold
935 }
936
937 pub fn try_set_release_threshold(&mut self, value: f32) -> Result<(), ButtonSettingsError> {
945 if (0.0..=self.press_threshold).contains(&value) {
946 self.release_threshold = value;
947 Ok(())
948 } else if !(0.0..1.0).contains(&value) {
949 Err(ButtonSettingsError::ReleaseThresholdOutOfRange(value))
950 } else {
951 Err(
952 ButtonSettingsError::ReleaseThresholdGreaterThanPressThreshold {
953 press_threshold: self.press_threshold,
954 release_threshold: value,
955 },
956 )
957 }
958 }
959
960 pub fn set_release_threshold(&mut self, value: f32) -> f32 {
965 self.try_set_release_threshold(value).ok();
966 self.release_threshold
967 }
968}
969
970#[derive(Debug, Clone, PartialEq)]
982#[cfg_attr(
983 feature = "bevy_reflect",
984 derive(Reflect),
985 reflect(Debug, PartialEq, Default, Clone)
986)]
987pub struct AxisSettings {
988 livezone_upperbound: f32,
990 deadzone_upperbound: f32,
992 deadzone_lowerbound: f32,
994 livezone_lowerbound: f32,
996 threshold: f32,
998}
999
1000impl Default for AxisSettings {
1001 fn default() -> Self {
1002 AxisSettings {
1003 livezone_upperbound: 1.0,
1004 deadzone_upperbound: 0.05,
1005 deadzone_lowerbound: -0.05,
1006 livezone_lowerbound: -1.0,
1007 threshold: 0.01,
1008 }
1009 }
1010}
1011
1012impl AxisSettings {
1013 pub fn new(
1035 livezone_lowerbound: f32,
1036 deadzone_lowerbound: f32,
1037 deadzone_upperbound: f32,
1038 livezone_upperbound: f32,
1039 threshold: f32,
1040 ) -> Result<AxisSettings, AxisSettingsError> {
1041 if !(-1.0..=0.0).contains(&livezone_lowerbound) {
1042 Err(AxisSettingsError::LiveZoneLowerBoundOutOfRange(
1043 livezone_lowerbound,
1044 ))
1045 } else if !(-1.0..=0.0).contains(&deadzone_lowerbound) {
1046 Err(AxisSettingsError::DeadZoneLowerBoundOutOfRange(
1047 deadzone_lowerbound,
1048 ))
1049 } else if !(0.0..=1.0).contains(&deadzone_upperbound) {
1050 Err(AxisSettingsError::DeadZoneUpperBoundOutOfRange(
1051 deadzone_upperbound,
1052 ))
1053 } else if !(0.0..=1.0).contains(&livezone_upperbound) {
1054 Err(AxisSettingsError::LiveZoneUpperBoundOutOfRange(
1055 livezone_upperbound,
1056 ))
1057 } else if livezone_lowerbound > deadzone_lowerbound {
1058 Err(
1059 AxisSettingsError::LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
1060 livezone_lowerbound,
1061 deadzone_lowerbound,
1062 },
1063 )
1064 } else if deadzone_upperbound > livezone_upperbound {
1065 Err(
1066 AxisSettingsError::DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
1067 livezone_upperbound,
1068 deadzone_upperbound,
1069 },
1070 )
1071 } else if !(0.0..=2.0).contains(&threshold) {
1072 Err(AxisSettingsError::Threshold(threshold))
1073 } else {
1074 Ok(Self {
1075 livezone_lowerbound,
1076 deadzone_lowerbound,
1077 deadzone_upperbound,
1078 livezone_upperbound,
1079 threshold,
1080 })
1081 }
1082 }
1083
1084 pub fn livezone_upperbound(&self) -> f32 {
1086 self.livezone_upperbound
1087 }
1088
1089 pub fn try_set_livezone_upperbound(&mut self, value: f32) -> Result<(), AxisSettingsError> {
1097 if !(0.0..=1.0).contains(&value) {
1098 Err(AxisSettingsError::LiveZoneUpperBoundOutOfRange(value))
1099 } else if value < self.deadzone_upperbound {
1100 Err(
1101 AxisSettingsError::DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
1102 livezone_upperbound: value,
1103 deadzone_upperbound: self.deadzone_upperbound,
1104 },
1105 )
1106 } else {
1107 self.livezone_upperbound = value;
1108 Ok(())
1109 }
1110 }
1111
1112 pub fn set_livezone_upperbound(&mut self, value: f32) -> f32 {
1118 self.try_set_livezone_upperbound(value).ok();
1119 self.livezone_upperbound
1120 }
1121
1122 pub fn deadzone_upperbound(&self) -> f32 {
1124 self.deadzone_upperbound
1125 }
1126
1127 pub fn try_set_deadzone_upperbound(&mut self, value: f32) -> Result<(), AxisSettingsError> {
1135 if !(0.0..=1.0).contains(&value) {
1136 Err(AxisSettingsError::DeadZoneUpperBoundOutOfRange(value))
1137 } else if self.livezone_upperbound < value {
1138 Err(
1139 AxisSettingsError::DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
1140 livezone_upperbound: self.livezone_upperbound,
1141 deadzone_upperbound: value,
1142 },
1143 )
1144 } else {
1145 self.deadzone_upperbound = value;
1146 Ok(())
1147 }
1148 }
1149
1150 pub fn set_deadzone_upperbound(&mut self, value: f32) -> f32 {
1156 self.try_set_deadzone_upperbound(value).ok();
1157 self.deadzone_upperbound
1158 }
1159
1160 pub fn livezone_lowerbound(&self) -> f32 {
1162 self.livezone_lowerbound
1163 }
1164
1165 pub fn try_set_livezone_lowerbound(&mut self, value: f32) -> Result<(), AxisSettingsError> {
1173 if !(-1.0..=0.0).contains(&value) {
1174 Err(AxisSettingsError::LiveZoneLowerBoundOutOfRange(value))
1175 } else if value > self.deadzone_lowerbound {
1176 Err(
1177 AxisSettingsError::LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
1178 livezone_lowerbound: value,
1179 deadzone_lowerbound: self.deadzone_lowerbound,
1180 },
1181 )
1182 } else {
1183 self.livezone_lowerbound = value;
1184 Ok(())
1185 }
1186 }
1187
1188 pub fn set_livezone_lowerbound(&mut self, value: f32) -> f32 {
1194 self.try_set_livezone_lowerbound(value).ok();
1195 self.livezone_lowerbound
1196 }
1197
1198 pub fn deadzone_lowerbound(&self) -> f32 {
1200 self.deadzone_lowerbound
1201 }
1202
1203 pub fn try_set_deadzone_lowerbound(&mut self, value: f32) -> Result<(), AxisSettingsError> {
1211 if !(-1.0..=0.0).contains(&value) {
1212 Err(AxisSettingsError::DeadZoneLowerBoundOutOfRange(value))
1213 } else if self.livezone_lowerbound > value {
1214 Err(
1215 AxisSettingsError::LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
1216 livezone_lowerbound: self.livezone_lowerbound,
1217 deadzone_lowerbound: value,
1218 },
1219 )
1220 } else {
1221 self.deadzone_lowerbound = value;
1222 Ok(())
1223 }
1224 }
1225
1226 pub fn set_deadzone_lowerbound(&mut self, value: f32) -> f32 {
1232 self.try_set_deadzone_lowerbound(value).ok();
1233 self.deadzone_lowerbound
1234 }
1235
1236 pub fn threshold(&self) -> f32 {
1238 self.threshold
1239 }
1240
1241 pub fn try_set_threshold(&mut self, value: f32) -> Result<(), AxisSettingsError> {
1247 if !(0.0..=2.0).contains(&value) {
1248 Err(AxisSettingsError::Threshold(value))
1249 } else {
1250 self.threshold = value;
1251 Ok(())
1252 }
1253 }
1254
1255 pub fn set_threshold(&mut self, value: f32) -> f32 {
1260 self.try_set_threshold(value).ok();
1261 self.threshold
1262 }
1263
1264 pub fn clamp(&self, raw_value: f32) -> f32 {
1266 if self.deadzone_lowerbound <= raw_value && raw_value <= self.deadzone_upperbound {
1267 0.0
1268 } else if raw_value >= self.livezone_upperbound {
1269 1.0
1270 } else if raw_value <= self.livezone_lowerbound {
1271 -1.0
1272 } else {
1273 raw_value
1274 }
1275 }
1276
1277 fn should_register_change(&self, new_raw_value: f32, old_raw_value: Option<f32>) -> bool {
1280 match old_raw_value {
1281 None => true,
1282 Some(old_raw_value) => ops::abs(new_raw_value - old_raw_value) >= self.threshold,
1283 }
1284 }
1285
1286 fn filter(
1291 &self,
1292 new_raw_value: f32,
1293 old_raw_value: Option<f32>,
1294 ) -> Option<FilteredAxisPosition> {
1295 let clamped_unscaled = self.clamp(new_raw_value);
1296 match self.should_register_change(clamped_unscaled, old_raw_value) {
1297 true => Some(FilteredAxisPosition {
1298 scaled: self.get_axis_position_from_value(clamped_unscaled),
1299 raw: new_raw_value,
1300 }),
1301 false => None,
1302 }
1303 }
1304
1305 #[inline(always)]
1306 fn get_axis_position_from_value(&self, value: f32) -> ScaledAxisWithDeadZonePosition {
1307 if value < self.deadzone_upperbound && value > self.deadzone_lowerbound {
1308 ScaledAxisWithDeadZonePosition::Dead
1309 } else if value > self.livezone_upperbound {
1310 ScaledAxisWithDeadZonePosition::AboveHigh
1311 } else if value < self.livezone_lowerbound {
1312 ScaledAxisWithDeadZonePosition::BelowLow
1313 } else if value >= self.deadzone_upperbound {
1314 ScaledAxisWithDeadZonePosition::High(linear_remapping(
1315 value,
1316 self.deadzone_upperbound..=self.livezone_upperbound,
1317 0.0..=1.0,
1318 ))
1319 } else if value <= self.deadzone_lowerbound {
1320 ScaledAxisWithDeadZonePosition::Low(linear_remapping(
1321 value,
1322 self.livezone_lowerbound..=self.deadzone_lowerbound,
1323 -1.0..=0.0,
1324 ))
1325 } else {
1326 unreachable!();
1327 }
1328 }
1329}
1330
1331fn linear_remapping(value: f32, old: RangeInclusive<f32>, new: RangeInclusive<f32>) -> f32 {
1333 ((value - old.start()) / (old.end() - old.start())) * (new.end() - new.start()) + new.start()
1335}
1336
1337#[derive(Debug, Clone, Copy)]
1338enum ScaledAxisWithDeadZonePosition {
1340 BelowLow,
1342 Low(f32),
1344 Dead,
1346 High(f32),
1348 AboveHigh,
1350}
1351
1352struct FilteredAxisPosition {
1353 scaled: ScaledAxisWithDeadZonePosition,
1354 raw: f32,
1355}
1356
1357impl ScaledAxisWithDeadZonePosition {
1358 fn to_f32(self) -> f32 {
1360 match self {
1361 ScaledAxisWithDeadZonePosition::BelowLow => -1.,
1362 ScaledAxisWithDeadZonePosition::Low(scaled)
1363 | ScaledAxisWithDeadZonePosition::High(scaled) => scaled,
1364 ScaledAxisWithDeadZonePosition::Dead => 0.,
1365 ScaledAxisWithDeadZonePosition::AboveHigh => 1.,
1366 }
1367 }
1368}
1369
1370#[derive(Debug, Clone, Copy)]
1371enum ScaledAxisPosition {
1373 ClampedLow,
1375 Scaled(f32),
1377 ClampedHigh,
1379}
1380
1381struct FilteredButtonAxisPosition {
1382 scaled: ScaledAxisPosition,
1383 raw: f32,
1384}
1385
1386impl ScaledAxisPosition {
1387 fn to_f32(self) -> f32 {
1389 match self {
1390 ScaledAxisPosition::ClampedLow => 0.,
1391 ScaledAxisPosition::Scaled(scaled) => scaled,
1392 ScaledAxisPosition::ClampedHigh => 1.,
1393 }
1394 }
1395}
1396
1397#[derive(Debug, Clone)]
1410#[cfg_attr(
1411 feature = "bevy_reflect",
1412 derive(Reflect),
1413 reflect(Debug, Default, Clone)
1414)]
1415pub struct ButtonAxisSettings {
1416 pub high: f32,
1418 pub low: f32,
1420 pub threshold: f32,
1422}
1423
1424impl Default for ButtonAxisSettings {
1425 fn default() -> Self {
1426 ButtonAxisSettings {
1427 high: 0.95,
1428 low: 0.05,
1429 threshold: 0.01,
1430 }
1431 }
1432}
1433
1434impl ButtonAxisSettings {
1435 fn clamp(&self, raw_value: f32) -> f32 {
1442 if raw_value <= self.low {
1443 return 0.0;
1444 }
1445 if raw_value >= self.high {
1446 return 1.0;
1447 }
1448
1449 raw_value
1450 }
1451
1452 fn should_register_change(&self, new_raw_value: f32, old_raw_value: Option<f32>) -> bool {
1455 match old_raw_value {
1456 None => true,
1457 Some(old_raw_value) => ops::abs(new_raw_value - old_raw_value) >= self.threshold,
1458 }
1459 }
1460
1461 fn filter(
1466 &self,
1467 new_raw_value: f32,
1468 old_raw_value: Option<f32>,
1469 ) -> Option<FilteredButtonAxisPosition> {
1470 let clamped_unscaled = self.clamp(new_raw_value);
1471 match self.should_register_change(clamped_unscaled, old_raw_value) {
1472 true => Some(FilteredButtonAxisPosition {
1473 scaled: self.get_axis_position_from_value(clamped_unscaled),
1474 raw: new_raw_value,
1475 }),
1476 false => None,
1477 }
1478 }
1479
1480 fn get_axis_position_from_value(&self, value: f32) -> ScaledAxisPosition {
1487 if value <= self.low {
1488 ScaledAxisPosition::ClampedLow
1489 } else if value >= self.high {
1490 ScaledAxisPosition::ClampedHigh
1491 } else {
1492 ScaledAxisPosition::Scaled(linear_remapping(value, self.low..=self.high, 0.0..=1.0))
1493 }
1494 }
1495}
1496
1497pub fn gamepad_connection_system(
1507 mut commands: Commands,
1508 mut connection_events: EventReader<GamepadConnectionEvent>,
1509) {
1510 for connection_event in connection_events.read() {
1511 let id = connection_event.gamepad;
1512 match &connection_event.connection {
1513 GamepadConnection::Connected {
1514 name,
1515 vendor_id,
1516 product_id,
1517 } => {
1518 let Ok(mut gamepad) = commands.get_entity(id) else {
1519 warn!("Gamepad {} removed before handling connection event.", id);
1520 continue;
1521 };
1522 gamepad.insert((
1523 Name::new(name.clone()),
1524 Gamepad {
1525 vendor_id: *vendor_id,
1526 product_id: *product_id,
1527 ..Default::default()
1528 },
1529 ));
1530 info!("Gamepad {} connected.", id);
1531 }
1532 GamepadConnection::Disconnected => {
1533 let Ok(mut gamepad) = commands.get_entity(id) else {
1534 warn!("Gamepad {} removed before handling disconnection event. You can ignore this if you manually removed it.", id);
1535 continue;
1536 };
1537 gamepad.remove::<Gamepad>();
1541 info!("Gamepad {} disconnected.", id);
1542 }
1543 }
1544 }
1545}
1546
1547#[derive(Debug, Clone, PartialEq)]
1552#[cfg_attr(
1553 feature = "bevy_reflect",
1554 derive(Reflect),
1555 reflect(Debug, PartialEq, Clone)
1556)]
1557#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1558#[cfg_attr(
1559 all(feature = "serialize", feature = "bevy_reflect"),
1560 reflect(Serialize, Deserialize)
1561)]
1562pub enum GamepadConnection {
1563 Connected {
1565 name: String,
1571
1572 vendor_id: Option<u16>,
1574
1575 product_id: Option<u16>,
1577 },
1578 Disconnected,
1580}
1581
1582pub fn gamepad_event_processing_system(
1585 mut gamepads: Query<(&mut Gamepad, &GamepadSettings)>,
1586 mut raw_events: EventReader<RawGamepadEvent>,
1587 mut processed_events: EventWriter<GamepadEvent>,
1588 mut processed_axis_events: EventWriter<GamepadAxisChangedEvent>,
1589 mut processed_digital_events: EventWriter<GamepadButtonStateChangedEvent>,
1590 mut processed_analog_events: EventWriter<GamepadButtonChangedEvent>,
1591) {
1592 for (mut gamepad, _) in gamepads.iter_mut() {
1594 gamepad.bypass_change_detection().digital.clear();
1595 }
1596
1597 for event in raw_events.read() {
1598 match event {
1599 RawGamepadEvent::Connection(send_event) => {
1601 processed_events.write(GamepadEvent::from(send_event.clone()));
1602 }
1603 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent {
1604 gamepad,
1605 axis,
1606 value,
1607 }) => {
1608 let (gamepad, axis, value) = (*gamepad, *axis, *value);
1609 let Ok((mut gamepad_axis, gamepad_settings)) = gamepads.get_mut(gamepad) else {
1610 continue;
1611 };
1612 let Some(filtered_value) = gamepad_settings
1613 .get_axis_settings(axis)
1614 .filter(value, gamepad_axis.get(axis))
1615 else {
1616 continue;
1617 };
1618 gamepad_axis.analog.set(axis, filtered_value.raw);
1619 let send_event =
1620 GamepadAxisChangedEvent::new(gamepad, axis, filtered_value.scaled.to_f32());
1621 processed_axis_events.write(send_event);
1622 processed_events.write(GamepadEvent::from(send_event));
1623 }
1624 RawGamepadEvent::Button(RawGamepadButtonChangedEvent {
1625 gamepad,
1626 button,
1627 value,
1628 }) => {
1629 let (gamepad, button, value) = (*gamepad, *button, *value);
1630 let Ok((mut gamepad_buttons, settings)) = gamepads.get_mut(gamepad) else {
1631 continue;
1632 };
1633 let Some(filtered_value) = settings
1634 .get_button_axis_settings(button)
1635 .filter(value, gamepad_buttons.get(button))
1636 else {
1637 continue;
1638 };
1639 let button_settings = settings.get_button_settings(button);
1640 gamepad_buttons.analog.set(button, filtered_value.raw);
1641
1642 if button_settings.is_released(filtered_value.raw) {
1643 if gamepad_buttons.pressed(button) {
1645 processed_digital_events.write(GamepadButtonStateChangedEvent::new(
1646 gamepad,
1647 button,
1648 ButtonState::Released,
1649 ));
1650 }
1651 gamepad_buttons.digital.release(button);
1654 } else if button_settings.is_pressed(filtered_value.raw) {
1655 if !gamepad_buttons.pressed(button) {
1657 processed_digital_events.write(GamepadButtonStateChangedEvent::new(
1658 gamepad,
1659 button,
1660 ButtonState::Pressed,
1661 ));
1662 }
1663 gamepad_buttons.digital.press(button);
1664 };
1665
1666 let button_state = if gamepad_buttons.digital.pressed(button) {
1667 ButtonState::Pressed
1668 } else {
1669 ButtonState::Released
1670 };
1671 let send_event = GamepadButtonChangedEvent::new(
1672 gamepad,
1673 button,
1674 button_state,
1675 filtered_value.scaled.to_f32(),
1676 );
1677 processed_analog_events.write(send_event);
1678 processed_events.write(GamepadEvent::from(send_event));
1679 }
1680 }
1681 }
1682}
1683
1684#[derive(Clone, Copy, Debug, PartialEq)]
1686#[cfg_attr(
1687 feature = "bevy_reflect",
1688 derive(Reflect),
1689 reflect(Debug, PartialEq, Clone)
1690)]
1691pub struct GamepadRumbleIntensity {
1692 pub strong_motor: f32,
1699 pub weak_motor: f32,
1706}
1707
1708impl GamepadRumbleIntensity {
1709 pub const MAX: Self = GamepadRumbleIntensity {
1711 strong_motor: 1.0,
1712 weak_motor: 1.0,
1713 };
1714
1715 pub const WEAK_MAX: Self = GamepadRumbleIntensity {
1717 strong_motor: 0.0,
1718 weak_motor: 1.0,
1719 };
1720
1721 pub const STRONG_MAX: Self = GamepadRumbleIntensity {
1723 strong_motor: 1.0,
1724 weak_motor: 0.0,
1725 };
1726
1727 pub const fn weak_motor(intensity: f32) -> Self {
1731 Self {
1732 weak_motor: intensity,
1733 strong_motor: 0.0,
1734 }
1735 }
1736
1737 pub const fn strong_motor(intensity: f32) -> Self {
1741 Self {
1742 strong_motor: intensity,
1743 weak_motor: 0.0,
1744 }
1745 }
1746}
1747
1748#[doc(alias = "haptic feedback")]
1774#[doc(alias = "force feedback")]
1775#[doc(alias = "vibration")]
1776#[doc(alias = "vibrate")]
1777#[derive(Event, Clone)]
1778#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Clone))]
1779pub enum GamepadRumbleRequest {
1780 Add {
1791 duration: Duration,
1793 intensity: GamepadRumbleIntensity,
1795 gamepad: Entity,
1797 },
1798 Stop {
1800 gamepad: Entity,
1802 },
1803}
1804
1805impl GamepadRumbleRequest {
1806 pub fn gamepad(&self) -> Entity {
1808 match self {
1809 Self::Add { gamepad, .. } | Self::Stop { gamepad } => *gamepad,
1810 }
1811 }
1812}
1813
1814#[cfg(test)]
1815mod tests {
1816 use super::{
1817 gamepad_connection_system, gamepad_event_processing_system, AxisSettings,
1818 AxisSettingsError, ButtonAxisSettings, ButtonSettings, ButtonSettingsError, Gamepad,
1819 GamepadAxis, GamepadAxisChangedEvent, GamepadButton, GamepadButtonChangedEvent,
1820 GamepadButtonStateChangedEvent,
1821 GamepadConnection::{Connected, Disconnected},
1822 GamepadConnectionEvent, GamepadEvent, GamepadSettings, RawGamepadAxisChangedEvent,
1823 RawGamepadButtonChangedEvent, RawGamepadEvent,
1824 };
1825 use crate::ButtonState;
1826 use alloc::string::ToString;
1827 use bevy_app::{App, PreUpdate};
1828 use bevy_ecs::entity::Entity;
1829 use bevy_ecs::event::Events;
1830 use bevy_ecs::schedule::IntoScheduleConfigs;
1831
1832 fn test_button_axis_settings_filter(
1833 settings: ButtonAxisSettings,
1834 new_raw_value: f32,
1835 old_raw_value: Option<f32>,
1836 expected: Option<f32>,
1837 ) {
1838 let actual = settings
1839 .filter(new_raw_value, old_raw_value)
1840 .map(|f| f.scaled.to_f32());
1841 assert_eq!(
1842 expected, actual,
1843 "Testing filtering for {settings:?} with new_raw_value = {new_raw_value:?}, old_raw_value = {old_raw_value:?}",
1844 );
1845 }
1846
1847 #[test]
1848 fn test_button_axis_settings_default_filter() {
1849 let cases = [
1850 (1.0, None, Some(1.0)),
1852 (0.99, None, Some(1.0)),
1853 (0.96, None, Some(1.0)),
1854 (0.95, None, Some(1.0)),
1855 (0.9499, None, Some(0.9998889)),
1857 (0.84, None, Some(0.87777776)),
1858 (0.43, None, Some(0.42222223)),
1859 (0.05001, None, Some(0.000011109644)),
1860 (0.05, None, Some(0.0)),
1862 (0.04, None, Some(0.0)),
1863 (0.01, None, Some(0.0)),
1864 (0.0, None, Some(0.0)),
1865 ];
1866
1867 for (new_raw_value, old_raw_value, expected) in cases {
1868 let settings = ButtonAxisSettings::default();
1869 test_button_axis_settings_filter(settings, new_raw_value, old_raw_value, expected);
1870 }
1871 }
1872
1873 #[test]
1874 fn test_button_axis_settings_default_filter_with_old_raw_value() {
1875 let cases = [
1876 (0.43, Some(0.44001), Some(0.42222223)),
1878 (0.43, Some(0.44), None),
1879 (0.43, Some(0.43), None),
1880 (0.43, Some(0.41999), Some(0.42222223)),
1881 (0.43, Some(0.17), Some(0.42222223)),
1882 (0.43, Some(0.84), Some(0.42222223)),
1883 (0.05, Some(0.055), Some(0.0)),
1884 (0.95, Some(0.945), Some(1.0)),
1885 ];
1886
1887 for (new_raw_value, old_raw_value, expected) in cases {
1888 let settings = ButtonAxisSettings::default();
1889 test_button_axis_settings_filter(settings, new_raw_value, old_raw_value, expected);
1890 }
1891 }
1892
1893 fn test_axis_settings_filter(
1894 settings: AxisSettings,
1895 new_raw_value: f32,
1896 old_raw_value: Option<f32>,
1897 expected: Option<f32>,
1898 ) {
1899 let actual = settings.filter(new_raw_value, old_raw_value);
1900 assert_eq!(
1901 expected, actual.map(|f| f.scaled.to_f32()),
1902 "Testing filtering for {settings:?} with new_raw_value = {new_raw_value:?}, old_raw_value = {old_raw_value:?}",
1903 );
1904 }
1905
1906 #[test]
1907 fn test_axis_settings_default_filter() {
1908 let cases = [
1910 (1.0, Some(1.0)),
1912 (0.99, Some(1.0)),
1913 (0.96, Some(1.0)),
1914 (0.95, Some(1.0)),
1915 (0.9499, Some(0.9998889)), (0.84, Some(0.87777776)), (0.43, Some(0.42222223)), (0.05001, Some(0.000011109644)), (0.05, Some(0.0)),
1924 (0.04, Some(0.0)),
1925 (0.01, Some(0.0)),
1926 (0.0, Some(0.0)),
1927 (-1.0, Some(-1.0)),
1930 (-0.99, Some(-1.0)),
1931 (-0.96, Some(-1.0)),
1932 (-0.95, Some(-1.0)),
1933 (-0.9499, Some(-0.9998889)), (-0.84, Some(-0.87777776)), (-0.43, Some(-0.42222226)), (-0.05001, Some(-0.000011146069)), (-0.05, Some(0.0)),
1940 (-0.04, Some(0.0)),
1941 (-0.01, Some(0.0)),
1942 ];
1943
1944 for (new_raw_value, expected) in cases {
1945 let settings = AxisSettings::new(-0.95, -0.05, 0.05, 0.95, 0.01).unwrap();
1946 test_axis_settings_filter(settings, new_raw_value, None, expected);
1947 }
1948 }
1949
1950 #[test]
1951 fn test_axis_settings_default_filter_with_old_raw_values() {
1952 let threshold = 0.01;
1953 let cases = [
1956 (0.43, Some(0.43 + threshold * 1.1), Some(0.42222223)),
1958 (0.43, Some(0.43 - threshold * 1.1), Some(0.42222223)),
1960 (0.43, Some(0.43 + threshold * 0.9), None),
1962 (0.43, Some(0.43 - threshold * 0.9), None),
1964 (-0.43, Some(-0.43 + threshold * 1.1), Some(-0.42222226)),
1966 (-0.43, Some(-0.43 - threshold * 1.1), Some(-0.42222226)),
1968 (-0.43, Some(-0.43 + threshold * 0.9), None),
1970 (-0.43, Some(-0.43 - threshold * 0.9), None),
1972 (0.05, Some(0.0), None),
1974 (0.06, Some(0.0), Some(0.0111111095)),
1975 (-0.05, Some(0.0), None),
1977 (-0.06, Some(0.0), Some(-0.011111081)),
1978 (0.95, Some(1.0), None),
1980 (0.94, Some(1.0), Some(0.9888889)),
1981 (-0.95, Some(-1.0), None),
1983 (-0.94, Some(-1.0), Some(-0.9888889)),
1984 ];
1985
1986 for (new_raw_value, old_raw_value, expected) in cases {
1987 let settings = AxisSettings::new(-0.95, -0.05, 0.05, 0.95, threshold).unwrap();
1988 test_axis_settings_filter(settings, new_raw_value, old_raw_value, expected);
1989 }
1990 }
1991
1992 #[test]
1993 fn test_button_settings_default_is_pressed() {
1994 let cases = [
1995 (1.0, true),
1996 (0.95, true),
1997 (0.9, true),
1998 (0.8, true),
1999 (0.75, true),
2000 (0.7, false),
2001 (0.65, false),
2002 (0.5, false),
2003 (0.0, false),
2004 ];
2005
2006 for (value, expected) in cases {
2007 let settings = ButtonSettings::default();
2008 let actual = settings.is_pressed(value);
2009
2010 assert_eq!(
2011 expected, actual,
2012 "testing ButtonSettings::is_pressed() for value: {value}",
2013 );
2014 }
2015 }
2016
2017 #[test]
2018 fn test_button_settings_default_is_released() {
2019 let cases = [
2020 (1.0, false),
2021 (0.95, false),
2022 (0.9, false),
2023 (0.8, false),
2024 (0.75, false),
2025 (0.7, false),
2026 (0.65, true),
2027 (0.5, true),
2028 (0.0, true),
2029 ];
2030
2031 for (value, expected) in cases {
2032 let settings = ButtonSettings::default();
2033 let actual = settings.is_released(value);
2034
2035 assert_eq!(
2036 expected, actual,
2037 "testing ButtonSettings::is_released() for value: {value}",
2038 );
2039 }
2040 }
2041
2042 #[test]
2043 fn test_new_button_settings_given_valid_parameters() {
2044 let cases = [
2045 (1.0, 0.0),
2046 (1.0, 1.0),
2047 (1.0, 0.9),
2048 (0.9, 0.9),
2049 (0.9, 0.0),
2050 (0.0, 0.0),
2051 ];
2052
2053 for (press_threshold, release_threshold) in cases {
2054 let bs = ButtonSettings::new(press_threshold, release_threshold);
2055 match bs {
2056 Ok(button_settings) => {
2057 assert_eq!(button_settings.press_threshold, press_threshold);
2058 assert_eq!(button_settings.release_threshold, release_threshold);
2059 }
2060 Err(_) => {
2061 panic!(
2062 "ButtonSettings::new({press_threshold}, {release_threshold}) should be valid"
2063 );
2064 }
2065 }
2066 }
2067 }
2068
2069 #[test]
2070 fn test_new_button_settings_given_invalid_parameters() {
2071 let cases = [
2072 (1.1, 0.0),
2073 (1.1, 1.0),
2074 (1.0, 1.1),
2075 (-1.0, 0.9),
2076 (-1.0, 0.0),
2077 (-1.0, -0.4),
2078 (0.9, 1.0),
2079 (0.0, 0.1),
2080 ];
2081
2082 for (press_threshold, release_threshold) in cases {
2083 let bs = ButtonSettings::new(press_threshold, release_threshold);
2084 match bs {
2085 Ok(_) => {
2086 panic!(
2087 "ButtonSettings::new({press_threshold}, {release_threshold}) should be invalid"
2088 );
2089 }
2090 Err(err_code) => match err_code {
2091 ButtonSettingsError::PressThresholdOutOfRange(_press_threshold) => {}
2092 ButtonSettingsError::ReleaseThresholdGreaterThanPressThreshold {
2093 press_threshold: _press_threshold,
2094 release_threshold: _release_threshold,
2095 } => {}
2096 ButtonSettingsError::ReleaseThresholdOutOfRange(_release_threshold) => {}
2097 },
2098 }
2099 }
2100 }
2101
2102 #[test]
2103 fn test_try_out_of_range_axis_settings() {
2104 let mut axis_settings = AxisSettings::default();
2105 assert_eq!(
2106 AxisSettings::new(-0.95, -0.05, 0.05, 0.95, 0.001),
2107 Ok(AxisSettings {
2108 livezone_lowerbound: -0.95,
2109 deadzone_lowerbound: -0.05,
2110 deadzone_upperbound: 0.05,
2111 livezone_upperbound: 0.95,
2112 threshold: 0.001,
2113 })
2114 );
2115 assert_eq!(
2116 Err(AxisSettingsError::LiveZoneLowerBoundOutOfRange(-2.0)),
2117 axis_settings.try_set_livezone_lowerbound(-2.0)
2118 );
2119 assert_eq!(
2120 Err(AxisSettingsError::LiveZoneLowerBoundOutOfRange(0.1)),
2121 axis_settings.try_set_livezone_lowerbound(0.1)
2122 );
2123 assert_eq!(
2124 Err(AxisSettingsError::DeadZoneLowerBoundOutOfRange(-2.0)),
2125 axis_settings.try_set_deadzone_lowerbound(-2.0)
2126 );
2127 assert_eq!(
2128 Err(AxisSettingsError::DeadZoneLowerBoundOutOfRange(0.1)),
2129 axis_settings.try_set_deadzone_lowerbound(0.1)
2130 );
2131
2132 assert_eq!(
2133 Err(AxisSettingsError::DeadZoneUpperBoundOutOfRange(-0.1)),
2134 axis_settings.try_set_deadzone_upperbound(-0.1)
2135 );
2136 assert_eq!(
2137 Err(AxisSettingsError::DeadZoneUpperBoundOutOfRange(1.1)),
2138 axis_settings.try_set_deadzone_upperbound(1.1)
2139 );
2140 assert_eq!(
2141 Err(AxisSettingsError::LiveZoneUpperBoundOutOfRange(-0.1)),
2142 axis_settings.try_set_livezone_upperbound(-0.1)
2143 );
2144 assert_eq!(
2145 Err(AxisSettingsError::LiveZoneUpperBoundOutOfRange(1.1)),
2146 axis_settings.try_set_livezone_upperbound(1.1)
2147 );
2148
2149 axis_settings.set_livezone_lowerbound(-0.7);
2150 axis_settings.set_deadzone_lowerbound(-0.3);
2151 assert_eq!(
2152 Err(
2153 AxisSettingsError::LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
2154 livezone_lowerbound: -0.1,
2155 deadzone_lowerbound: -0.3,
2156 }
2157 ),
2158 axis_settings.try_set_livezone_lowerbound(-0.1)
2159 );
2160 assert_eq!(
2161 Err(
2162 AxisSettingsError::LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
2163 livezone_lowerbound: -0.7,
2164 deadzone_lowerbound: -0.9
2165 }
2166 ),
2167 axis_settings.try_set_deadzone_lowerbound(-0.9)
2168 );
2169
2170 axis_settings.set_deadzone_upperbound(0.3);
2171 axis_settings.set_livezone_upperbound(0.7);
2172 assert_eq!(
2173 Err(
2174 AxisSettingsError::DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
2175 deadzone_upperbound: 0.8,
2176 livezone_upperbound: 0.7
2177 }
2178 ),
2179 axis_settings.try_set_deadzone_upperbound(0.8)
2180 );
2181 assert_eq!(
2182 Err(
2183 AxisSettingsError::DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
2184 deadzone_upperbound: 0.3,
2185 livezone_upperbound: 0.1
2186 }
2187 ),
2188 axis_settings.try_set_livezone_upperbound(0.1)
2189 );
2190 }
2191
2192 struct TestContext {
2193 pub app: App,
2194 }
2195
2196 impl TestContext {
2197 pub fn new() -> Self {
2198 let mut app = App::new();
2199 app.add_systems(
2200 PreUpdate,
2201 (
2202 gamepad_connection_system,
2203 gamepad_event_processing_system.after(gamepad_connection_system),
2204 ),
2205 )
2206 .add_event::<GamepadEvent>()
2207 .add_event::<GamepadConnectionEvent>()
2208 .add_event::<RawGamepadButtonChangedEvent>()
2209 .add_event::<GamepadButtonChangedEvent>()
2210 .add_event::<GamepadButtonStateChangedEvent>()
2211 .add_event::<GamepadAxisChangedEvent>()
2212 .add_event::<RawGamepadAxisChangedEvent>()
2213 .add_event::<RawGamepadEvent>();
2214 Self { app }
2215 }
2216
2217 pub fn update(&mut self) {
2218 self.app.update();
2219 }
2220
2221 pub fn send_gamepad_connection_event(&mut self, gamepad: Option<Entity>) -> Entity {
2222 let gamepad = gamepad.unwrap_or_else(|| self.app.world_mut().spawn_empty().id());
2223 self.app
2224 .world_mut()
2225 .resource_mut::<Events<GamepadConnectionEvent>>()
2226 .send(GamepadConnectionEvent::new(
2227 gamepad,
2228 Connected {
2229 name: "Test gamepad".to_string(),
2230 vendor_id: None,
2231 product_id: None,
2232 },
2233 ));
2234 gamepad
2235 }
2236
2237 pub fn send_gamepad_disconnection_event(&mut self, gamepad: Entity) {
2238 self.app
2239 .world_mut()
2240 .resource_mut::<Events<GamepadConnectionEvent>>()
2241 .send(GamepadConnectionEvent::new(gamepad, Disconnected));
2242 }
2243
2244 pub fn send_raw_gamepad_event(&mut self, event: RawGamepadEvent) {
2245 self.app
2246 .world_mut()
2247 .resource_mut::<Events<RawGamepadEvent>>()
2248 .send(event);
2249 }
2250
2251 pub fn send_raw_gamepad_event_batch(
2252 &mut self,
2253 events: impl IntoIterator<Item = RawGamepadEvent>,
2254 ) {
2255 self.app
2256 .world_mut()
2257 .resource_mut::<Events<RawGamepadEvent>>()
2258 .send_batch(events);
2259 }
2260 }
2261
2262 #[test]
2263 fn connection_event() {
2264 let mut ctx = TestContext::new();
2265 assert_eq!(
2266 ctx.app
2267 .world_mut()
2268 .query::<&Gamepad>()
2269 .iter(ctx.app.world())
2270 .len(),
2271 0
2272 );
2273 ctx.send_gamepad_connection_event(None);
2274 ctx.update();
2275 assert_eq!(
2276 ctx.app
2277 .world_mut()
2278 .query::<(&Gamepad, &GamepadSettings)>()
2279 .iter(ctx.app.world())
2280 .len(),
2281 1
2282 );
2283 }
2284
2285 #[test]
2286 fn disconnection_event() {
2287 let mut ctx = TestContext::new();
2288 assert_eq!(
2289 ctx.app
2290 .world_mut()
2291 .query::<&Gamepad>()
2292 .iter(ctx.app.world())
2293 .len(),
2294 0
2295 );
2296 let entity = ctx.send_gamepad_connection_event(None);
2297 ctx.update();
2298 assert_eq!(
2299 ctx.app
2300 .world_mut()
2301 .query::<(&Gamepad, &GamepadSettings)>()
2302 .iter(ctx.app.world())
2303 .len(),
2304 1
2305 );
2306 ctx.send_gamepad_disconnection_event(entity);
2307 ctx.update();
2308 assert!(ctx
2310 .app
2311 .world_mut()
2312 .query::<&Gamepad>()
2313 .get(ctx.app.world(), entity)
2314 .is_err());
2315 assert!(ctx
2317 .app
2318 .world_mut()
2319 .query::<&GamepadSettings>()
2320 .get(ctx.app.world(), entity)
2321 .is_ok());
2322
2323 ctx.send_gamepad_disconnection_event(entity);
2325 ctx.update();
2326 assert!(ctx
2327 .app
2328 .world_mut()
2329 .query::<&Gamepad>()
2330 .get(ctx.app.world(), entity)
2331 .is_err());
2332 assert!(ctx
2333 .app
2334 .world_mut()
2335 .query::<&GamepadSettings>()
2336 .get(ctx.app.world(), entity)
2337 .is_ok());
2338 }
2339
2340 #[test]
2341 fn connection_disconnection_frame_event() {
2342 let mut ctx = TestContext::new();
2343 assert_eq!(
2344 ctx.app
2345 .world_mut()
2346 .query::<&Gamepad>()
2347 .iter(ctx.app.world())
2348 .len(),
2349 0
2350 );
2351 let entity = ctx.send_gamepad_connection_event(None);
2352 ctx.send_gamepad_disconnection_event(entity);
2353 ctx.update();
2354 assert!(ctx
2356 .app
2357 .world_mut()
2358 .query::<&Gamepad>()
2359 .get(ctx.app.world(), entity)
2360 .is_err());
2361 assert!(ctx
2363 .app
2364 .world_mut()
2365 .query::<&GamepadSettings>()
2366 .get(ctx.app.world(), entity)
2367 .is_ok());
2368 }
2369
2370 #[test]
2371 fn reconnection_event() {
2372 let button_settings = ButtonSettings::new(0.7, 0.2).expect("correct parameters");
2373 let mut ctx = TestContext::new();
2374 assert_eq!(
2375 ctx.app
2376 .world_mut()
2377 .query::<&Gamepad>()
2378 .iter(ctx.app.world())
2379 .len(),
2380 0
2381 );
2382 let entity = ctx.send_gamepad_connection_event(None);
2383 ctx.update();
2384 let mut settings = ctx
2385 .app
2386 .world_mut()
2387 .query::<&mut GamepadSettings>()
2388 .get_mut(ctx.app.world_mut(), entity)
2389 .expect("be alive");
2390 assert_ne!(settings.default_button_settings, button_settings);
2391 settings.default_button_settings = button_settings.clone();
2392 ctx.send_gamepad_disconnection_event(entity);
2393 ctx.update();
2394 assert_eq!(
2395 ctx.app
2396 .world_mut()
2397 .query::<&Gamepad>()
2398 .iter(ctx.app.world())
2399 .len(),
2400 0
2401 );
2402 ctx.send_gamepad_connection_event(Some(entity));
2403 ctx.update();
2404 let settings = ctx
2405 .app
2406 .world_mut()
2407 .query::<&GamepadSettings>()
2408 .get(ctx.app.world(), entity)
2409 .expect("be alive");
2410 assert_eq!(settings.default_button_settings, button_settings);
2411 }
2412
2413 #[test]
2414 fn reconnection_same_frame_event() {
2415 let mut ctx = TestContext::new();
2416 assert_eq!(
2417 ctx.app
2418 .world_mut()
2419 .query::<&Gamepad>()
2420 .iter(ctx.app.world())
2421 .len(),
2422 0
2423 );
2424 let entity = ctx.send_gamepad_connection_event(None);
2425 ctx.send_gamepad_disconnection_event(entity);
2426 ctx.update();
2427 assert_eq!(
2428 ctx.app
2429 .world_mut()
2430 .query::<&Gamepad>()
2431 .iter(ctx.app.world())
2432 .len(),
2433 0
2434 );
2435 assert!(ctx
2436 .app
2437 .world_mut()
2438 .query::<(Entity, &GamepadSettings)>()
2439 .get(ctx.app.world(), entity)
2440 .is_ok());
2441 }
2442
2443 #[test]
2444 fn gamepad_axis_valid() {
2445 let mut ctx = TestContext::new();
2446
2447 let entity = ctx.send_gamepad_connection_event(None);
2449 ctx.app
2450 .world_mut()
2451 .resource_mut::<Events<RawGamepadEvent>>()
2452 .send_batch([
2453 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2454 entity,
2455 GamepadAxis::LeftStickY,
2456 0.5,
2457 )),
2458 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2459 entity,
2460 GamepadAxis::RightStickX,
2461 0.6,
2462 )),
2463 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2464 entity,
2465 GamepadAxis::RightZ,
2466 -0.4,
2467 )),
2468 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2469 entity,
2470 GamepadAxis::RightStickY,
2471 -0.8,
2472 )),
2473 ]);
2474 ctx.update();
2475 assert_eq!(
2476 ctx.app
2477 .world()
2478 .resource::<Events<GamepadAxisChangedEvent>>()
2479 .len(),
2480 4
2481 );
2482 }
2483
2484 #[test]
2485 fn gamepad_axis_threshold_filter() {
2486 let mut ctx = TestContext::new();
2487
2488 let entity = ctx.send_gamepad_connection_event(None);
2490 let settings = GamepadSettings::default().default_axis_settings;
2491 let base_value = 0.5;
2493 let events = [
2494 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2496 entity,
2497 GamepadAxis::LeftStickX,
2498 base_value,
2499 )),
2500 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2502 entity,
2503 GamepadAxis::LeftStickX,
2504 base_value + settings.threshold - 0.01,
2505 )),
2506 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2508 entity,
2509 GamepadAxis::LeftStickX,
2510 base_value + settings.threshold + 0.01,
2511 )),
2512 ];
2513 ctx.app
2514 .world_mut()
2515 .resource_mut::<Events<RawGamepadEvent>>()
2516 .send_batch(events);
2517 ctx.update();
2518 assert_eq!(
2519 ctx.app
2520 .world()
2521 .resource::<Events<GamepadAxisChangedEvent>>()
2522 .len(),
2523 2
2524 );
2525 }
2526
2527 #[test]
2528 fn gamepad_axis_deadzone_filter() {
2529 let mut ctx = TestContext::new();
2530
2531 let entity = ctx.send_gamepad_connection_event(None);
2533 let settings = GamepadSettings::default().default_axis_settings;
2534
2535 let events = [
2537 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2539 entity,
2540 GamepadAxis::LeftStickX,
2541 settings.deadzone_upperbound - 0.01,
2542 )),
2543 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2545 entity,
2546 GamepadAxis::LeftStickX,
2547 settings.deadzone_lowerbound + 0.01,
2548 )),
2549 ];
2550 ctx.app
2551 .world_mut()
2552 .resource_mut::<Events<RawGamepadEvent>>()
2553 .send_batch(events);
2554 ctx.update();
2555 assert_eq!(
2556 ctx.app
2557 .world()
2558 .resource::<Events<GamepadAxisChangedEvent>>()
2559 .len(),
2560 0
2561 );
2562 }
2563
2564 #[test]
2565 fn gamepad_axis_deadzone_rounded() {
2566 let mut ctx = TestContext::new();
2567
2568 let entity = ctx.send_gamepad_connection_event(None);
2570 let settings = GamepadSettings::default().default_axis_settings;
2571
2572 let events = [
2574 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2575 entity,
2576 GamepadAxis::LeftStickX,
2577 1.0,
2578 )),
2579 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2581 entity,
2582 GamepadAxis::LeftStickX,
2583 settings.deadzone_upperbound - 0.01,
2584 )),
2585 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2586 entity,
2587 GamepadAxis::LeftStickX,
2588 1.0,
2589 )),
2590 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2592 entity,
2593 GamepadAxis::LeftStickX,
2594 settings.deadzone_lowerbound + 0.01,
2595 )),
2596 ];
2597 let results = [1.0, 0.0, 1.0, 0.0];
2598 ctx.app
2599 .world_mut()
2600 .resource_mut::<Events<RawGamepadEvent>>()
2601 .send_batch(events);
2602 ctx.update();
2603
2604 let events = ctx
2605 .app
2606 .world()
2607 .resource::<Events<GamepadAxisChangedEvent>>();
2608 let mut event_reader = events.get_cursor();
2609 for (event, result) in event_reader.read(events).zip(results) {
2610 assert_eq!(event.value, result);
2611 }
2612 assert_eq!(
2613 ctx.app
2614 .world()
2615 .resource::<Events<GamepadAxisChangedEvent>>()
2616 .len(),
2617 4
2618 );
2619 }
2620
2621 #[test]
2622 fn gamepad_axis_livezone_filter() {
2623 let mut ctx = TestContext::new();
2624
2625 let entity = ctx.send_gamepad_connection_event(None);
2627 let settings = GamepadSettings::default().default_axis_settings;
2628
2629 let events = [
2631 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2632 entity,
2633 GamepadAxis::LeftStickX,
2634 1.0,
2635 )),
2636 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2638 entity,
2639 GamepadAxis::LeftStickX,
2640 settings.livezone_upperbound + 0.01,
2641 )),
2642 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2643 entity,
2644 GamepadAxis::LeftStickX,
2645 -1.0,
2646 )),
2647 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2649 entity,
2650 GamepadAxis::LeftStickX,
2651 settings.livezone_lowerbound - 0.01,
2652 )),
2653 ];
2654 ctx.app
2655 .world_mut()
2656 .resource_mut::<Events<RawGamepadEvent>>()
2657 .send_batch(events);
2658 ctx.update();
2659 assert_eq!(
2660 ctx.app
2661 .world()
2662 .resource::<Events<GamepadAxisChangedEvent>>()
2663 .len(),
2664 2
2665 );
2666 }
2667
2668 #[test]
2669 fn gamepad_axis_livezone_rounded() {
2670 let mut ctx = TestContext::new();
2671
2672 let entity = ctx.send_gamepad_connection_event(None);
2674 let settings = GamepadSettings::default().default_axis_settings;
2675
2676 let events = [
2678 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2680 entity,
2681 GamepadAxis::LeftStickX,
2682 settings.livezone_upperbound + 0.01,
2683 )),
2684 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2686 entity,
2687 GamepadAxis::LeftStickX,
2688 settings.livezone_lowerbound - 0.01,
2689 )),
2690 ];
2691 let results = [1.0, -1.0];
2692 ctx.app
2693 .world_mut()
2694 .resource_mut::<Events<RawGamepadEvent>>()
2695 .send_batch(events);
2696 ctx.update();
2697
2698 let events = ctx
2699 .app
2700 .world()
2701 .resource::<Events<GamepadAxisChangedEvent>>();
2702 let mut event_reader = events.get_cursor();
2703 for (event, result) in event_reader.read(events).zip(results) {
2704 assert_eq!(event.value, result);
2705 }
2706 assert_eq!(
2707 ctx.app
2708 .world()
2709 .resource::<Events<GamepadAxisChangedEvent>>()
2710 .len(),
2711 2
2712 );
2713 }
2714
2715 #[test]
2716 fn gamepad_buttons_pressed() {
2717 let mut ctx = TestContext::new();
2718
2719 let entity = ctx.send_gamepad_connection_event(None);
2721 let digital_settings = GamepadSettings::default().default_button_settings;
2722
2723 let events = [RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2724 entity,
2725 GamepadButton::DPadDown,
2726 digital_settings.press_threshold,
2727 ))];
2728 ctx.app
2729 .world_mut()
2730 .resource_mut::<Events<RawGamepadEvent>>()
2731 .send_batch(events);
2732 ctx.update();
2733
2734 assert_eq!(
2735 ctx.app
2736 .world()
2737 .resource::<Events<GamepadButtonStateChangedEvent>>()
2738 .len(),
2739 1
2740 );
2741 let events = ctx
2742 .app
2743 .world()
2744 .resource::<Events<GamepadButtonStateChangedEvent>>();
2745 let mut event_reader = events.get_cursor();
2746 for event in event_reader.read(events) {
2747 assert_eq!(event.button, GamepadButton::DPadDown);
2748 assert_eq!(event.state, ButtonState::Pressed);
2749 }
2750 assert!(ctx
2751 .app
2752 .world_mut()
2753 .query::<&Gamepad>()
2754 .get(ctx.app.world(), entity)
2755 .unwrap()
2756 .pressed(GamepadButton::DPadDown));
2757
2758 ctx.app
2759 .world_mut()
2760 .resource_mut::<Events<GamepadButtonStateChangedEvent>>()
2761 .clear();
2762 ctx.update();
2763
2764 assert_eq!(
2765 ctx.app
2766 .world()
2767 .resource::<Events<GamepadButtonStateChangedEvent>>()
2768 .len(),
2769 0
2770 );
2771 assert!(ctx
2772 .app
2773 .world_mut()
2774 .query::<&Gamepad>()
2775 .get(ctx.app.world(), entity)
2776 .unwrap()
2777 .pressed(GamepadButton::DPadDown));
2778 }
2779
2780 #[test]
2781 fn gamepad_buttons_just_pressed() {
2782 let mut ctx = TestContext::new();
2783
2784 let entity = ctx.send_gamepad_connection_event(None);
2786 let digital_settings = GamepadSettings::default().default_button_settings;
2787
2788 ctx.send_raw_gamepad_event(RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2789 entity,
2790 GamepadButton::DPadDown,
2791 digital_settings.press_threshold,
2792 )));
2793 ctx.update();
2794
2795 assert!(ctx
2797 .app
2798 .world_mut()
2799 .query::<&Gamepad>()
2800 .get(ctx.app.world(), entity)
2801 .unwrap()
2802 .just_pressed(GamepadButton::DPadDown));
2803 ctx.update();
2804
2805 assert!(!ctx
2807 .app
2808 .world_mut()
2809 .query::<&Gamepad>()
2810 .get(ctx.app.world(), entity)
2811 .unwrap()
2812 .just_pressed(GamepadButton::DPadDown));
2813 }
2814 #[test]
2815 fn gamepad_buttons_released() {
2816 let mut ctx = TestContext::new();
2817
2818 let entity = ctx.send_gamepad_connection_event(None);
2820 let digital_settings = GamepadSettings::default().default_button_settings;
2821
2822 ctx.send_raw_gamepad_event(RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2823 entity,
2824 GamepadButton::DPadDown,
2825 digital_settings.press_threshold,
2826 )));
2827 ctx.update();
2828
2829 ctx.app
2830 .world_mut()
2831 .resource_mut::<Events<GamepadButtonStateChangedEvent>>()
2832 .clear();
2833 ctx.send_raw_gamepad_event(RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2834 entity,
2835 GamepadButton::DPadDown,
2836 digital_settings.release_threshold - 0.01,
2837 )));
2838 ctx.update();
2839 assert_eq!(
2840 ctx.app
2841 .world()
2842 .resource::<Events<GamepadButtonStateChangedEvent>>()
2843 .len(),
2844 1
2845 );
2846 let events = ctx
2847 .app
2848 .world()
2849 .resource::<Events<GamepadButtonStateChangedEvent>>();
2850 let mut event_reader = events.get_cursor();
2851 for event in event_reader.read(events) {
2852 assert_eq!(event.button, GamepadButton::DPadDown);
2853 assert_eq!(event.state, ButtonState::Released);
2854 }
2855 assert!(!ctx
2856 .app
2857 .world_mut()
2858 .query::<&Gamepad>()
2859 .get(ctx.app.world(), entity)
2860 .unwrap()
2861 .pressed(GamepadButton::DPadDown));
2862 ctx.app
2863 .world_mut()
2864 .resource_mut::<Events<GamepadButtonStateChangedEvent>>()
2865 .clear();
2866 ctx.update();
2867
2868 assert_eq!(
2869 ctx.app
2870 .world()
2871 .resource::<Events<GamepadButtonStateChangedEvent>>()
2872 .len(),
2873 0
2874 );
2875 }
2876
2877 #[test]
2878 fn gamepad_buttons_just_released() {
2879 let mut ctx = TestContext::new();
2880
2881 let entity = ctx.send_gamepad_connection_event(None);
2883 let digital_settings = GamepadSettings::default().default_button_settings;
2884
2885 ctx.send_raw_gamepad_event_batch([
2886 RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2887 entity,
2888 GamepadButton::DPadDown,
2889 digital_settings.press_threshold,
2890 )),
2891 RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2892 entity,
2893 GamepadButton::DPadDown,
2894 digital_settings.release_threshold - 0.01,
2895 )),
2896 ]);
2897 ctx.update();
2898
2899 assert!(ctx
2901 .app
2902 .world_mut()
2903 .query::<&Gamepad>()
2904 .get(ctx.app.world(), entity)
2905 .unwrap()
2906 .just_released(GamepadButton::DPadDown));
2907 ctx.update();
2908
2909 assert!(!ctx
2911 .app
2912 .world_mut()
2913 .query::<&Gamepad>()
2914 .get(ctx.app.world(), entity)
2915 .unwrap()
2916 .just_released(GamepadButton::DPadDown));
2917 }
2918
2919 #[test]
2920 fn gamepad_buttons_axis() {
2921 let mut ctx = TestContext::new();
2922
2923 let entity = ctx.send_gamepad_connection_event(None);
2925 let digital_settings = GamepadSettings::default().default_button_settings;
2926 let analog_settings = GamepadSettings::default().default_button_axis_settings;
2927
2928 let events = [
2930 RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2932 entity,
2933 GamepadButton::DPadDown,
2934 digital_settings.press_threshold,
2935 )),
2936 RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2938 entity,
2939 GamepadButton::DPadDown,
2940 digital_settings.release_threshold,
2941 )),
2942 RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2944 entity,
2945 GamepadButton::DPadDown,
2946 digital_settings.release_threshold - analog_settings.threshold * 1.01,
2947 )),
2948 RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2950 entity,
2951 GamepadButton::DPadDown,
2952 digital_settings.release_threshold - (analog_settings.threshold * 1.5),
2953 )),
2954 RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2956 entity,
2957 GamepadButton::DPadDown,
2958 digital_settings.release_threshold - (analog_settings.threshold * 2.02),
2959 )),
2960 ];
2961 ctx.send_raw_gamepad_event_batch(events);
2962 ctx.update();
2963 assert_eq!(
2964 ctx.app
2965 .world()
2966 .resource::<Events<GamepadButtonStateChangedEvent>>()
2967 .len(),
2968 2
2969 );
2970 assert_eq!(
2971 ctx.app
2972 .world()
2973 .resource::<Events<GamepadButtonChangedEvent>>()
2974 .len(),
2975 4
2976 );
2977 }
2978}