1use crate::{Axis, ButtonInput, ButtonState};
4use bevy_core::Name;
5use bevy_ecs::{
6 change_detection::DetectChangesMut,
7 component::Component,
8 entity::Entity,
9 event::{Event, EventReader, EventWriter},
10 system::{Commands, Query},
11};
12use bevy_math::Vec2;
13#[cfg(feature = "bevy_reflect")]
14use bevy_reflect::{std_traits::ReflectDefault, Reflect};
15#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
16use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
17use bevy_utils::{
18 tracing::{info, warn},
19 Duration, HashMap,
20};
21use derive_more::derive::{Display, Error, From};
22
23#[derive(Event, Debug, Clone, PartialEq, From)]
31#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
32#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
33#[cfg_attr(
34 all(feature = "serialize", feature = "bevy_reflect"),
35 reflect(Serialize, Deserialize)
36)]
37pub enum GamepadEvent {
38 Connection(GamepadConnectionEvent),
40 Button(GamepadButtonChangedEvent),
42 Axis(GamepadAxisChangedEvent),
44}
45
46#[derive(Event, Debug, Clone, PartialEq, Reflect, From)]
54#[reflect(Debug, PartialEq)]
55#[cfg_attr(
56 feature = "serialize",
57 derive(serde::Serialize, serde::Deserialize),
58 reflect(Serialize, Deserialize)
59)]
60pub enum RawGamepadEvent {
61 Connection(GamepadConnectionEvent),
63 Button(RawGamepadButtonChangedEvent),
65 Axis(RawGamepadAxisChangedEvent),
67}
68
69#[derive(Event, Debug, Copy, Clone, PartialEq, Reflect)]
71#[reflect(Debug, PartialEq)]
72#[cfg_attr(
73 feature = "serialize",
74 derive(serde::Serialize, serde::Deserialize),
75 reflect(Serialize, Deserialize)
76)]
77pub struct RawGamepadButtonChangedEvent {
78 pub gamepad: Entity,
80 pub button: GamepadButton,
82 pub value: f32,
84}
85
86impl RawGamepadButtonChangedEvent {
87 pub fn new(gamepad: Entity, button_type: GamepadButton, value: f32) -> Self {
89 Self {
90 gamepad,
91 button: button_type,
92 value,
93 }
94 }
95}
96
97#[derive(Event, Debug, Copy, Clone, PartialEq, Reflect)]
99#[reflect(Debug, PartialEq)]
100#[cfg_attr(
101 feature = "serialize",
102 derive(serde::Serialize, serde::Deserialize),
103 reflect(Serialize, Deserialize)
104)]
105pub struct RawGamepadAxisChangedEvent {
106 pub gamepad: Entity,
108 pub axis: GamepadAxis,
110 pub value: f32,
112}
113
114impl RawGamepadAxisChangedEvent {
115 pub fn new(gamepad: Entity, axis_type: GamepadAxis, value: f32) -> Self {
117 Self {
118 gamepad,
119 axis: axis_type,
120 value,
121 }
122 }
123}
124
125#[derive(Event, Debug, Clone, PartialEq, Reflect)]
128#[reflect(Debug, PartialEq)]
129#[cfg_attr(
130 feature = "serialize",
131 derive(serde::Serialize, serde::Deserialize),
132 reflect(Serialize, Deserialize)
133)]
134pub struct GamepadConnectionEvent {
135 pub gamepad: Entity,
137 pub connection: GamepadConnection,
139}
140
141impl GamepadConnectionEvent {
142 pub fn new(gamepad: Entity, connection: GamepadConnection) -> Self {
144 Self {
145 gamepad,
146 connection,
147 }
148 }
149
150 pub fn connected(&self) -> bool {
152 matches!(self.connection, GamepadConnection::Connected { .. })
153 }
154
155 pub fn disconnected(&self) -> bool {
157 !self.connected()
158 }
159}
160
161#[derive(Event, Debug, Clone, Copy, PartialEq, Eq, Reflect)]
163#[reflect(Debug, PartialEq)]
164#[cfg_attr(
165 feature = "serialize",
166 derive(serde::Serialize, serde::Deserialize),
167 reflect(Serialize, Deserialize)
168)]
169pub struct GamepadButtonStateChangedEvent {
170 pub entity: Entity,
172 pub button: GamepadButton,
174 pub state: ButtonState,
176}
177
178impl GamepadButtonStateChangedEvent {
179 pub fn new(entity: Entity, button: GamepadButton, state: ButtonState) -> Self {
181 Self {
182 entity,
183 button,
184 state,
185 }
186 }
187}
188
189#[derive(Event, Debug, Clone, Copy, PartialEq, Reflect)]
191#[reflect(Debug, PartialEq)]
192#[cfg_attr(
193 feature = "serialize",
194 derive(serde::Serialize, serde::Deserialize),
195 reflect(Serialize, Deserialize)
196)]
197pub struct GamepadButtonChangedEvent {
198 pub entity: Entity,
200 pub button: GamepadButton,
202 pub state: ButtonState,
204 pub value: f32,
206}
207
208impl GamepadButtonChangedEvent {
209 pub fn new(entity: Entity, button: GamepadButton, state: ButtonState, value: f32) -> Self {
211 Self {
212 entity,
213 button,
214 state,
215 value,
216 }
217 }
218}
219
220#[derive(Event, Debug, Clone, Copy, PartialEq, Reflect)]
222#[reflect(Debug, PartialEq)]
223#[cfg_attr(
224 feature = "serialize",
225 derive(serde::Serialize, serde::Deserialize),
226 reflect(Serialize, Deserialize)
227)]
228pub struct GamepadAxisChangedEvent {
229 pub entity: Entity,
231 pub axis: GamepadAxis,
233 pub value: f32,
235}
236
237impl GamepadAxisChangedEvent {
238 pub fn new(entity: Entity, axis: GamepadAxis, value: f32) -> Self {
240 Self {
241 entity,
242 axis,
243 value,
244 }
245 }
246}
247
248#[derive(Error, Display, Debug, PartialEq)]
250pub enum AxisSettingsError {
251 #[display("invalid livezone_lowerbound {_0}, expected value [-1.0..=0.0]")]
253 #[error(ignore)]
254 LiveZoneLowerBoundOutOfRange(f32),
255 #[display("invalid deadzone_lowerbound {_0}, expected value [-1.0..=0.0]")]
257 #[error(ignore)]
258 DeadZoneLowerBoundOutOfRange(f32),
259 #[display("invalid deadzone_upperbound {_0}, expected value [0.0..=1.0]")]
261 #[error(ignore)]
262 DeadZoneUpperBoundOutOfRange(f32),
263 #[display("invalid livezone_upperbound {_0}, expected value [0.0..=1.0]")]
265 #[error(ignore)]
266 LiveZoneUpperBoundOutOfRange(f32),
267 #[display("invalid parameter values livezone_lowerbound {} deadzone_lowerbound {}, expected livezone_lowerbound <= deadzone_lowerbound", livezone_lowerbound, deadzone_lowerbound)]
269 LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
270 livezone_lowerbound: f32,
272 deadzone_lowerbound: f32,
274 },
275 #[display("invalid parameter values livezone_upperbound {} deadzone_upperbound {}, expected deadzone_upperbound <= livezone_upperbound", livezone_upperbound, deadzone_upperbound)]
277 DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
278 livezone_upperbound: f32,
280 deadzone_upperbound: f32,
282 },
283 #[display("invalid threshold {_0}, expected 0.0 <= threshold <= 2.0")]
285 #[error(ignore)]
286 Threshold(f32),
287}
288
289#[derive(Error, Display, Debug, PartialEq)]
291pub enum ButtonSettingsError {
292 #[display("invalid release_threshold {_0}, expected value [0.0..=1.0]")]
294 #[error(ignore)]
295 ReleaseThresholdOutOfRange(f32),
296 #[display("invalid press_threshold {_0}, expected [0.0..=1.0]")]
298 #[error(ignore)]
299 PressThresholdOutOfRange(f32),
300 #[display("invalid parameter values release_threshold {} press_threshold {}, expected release_threshold <= press_threshold", release_threshold, press_threshold)]
302 ReleaseThresholdGreaterThanPressThreshold {
303 press_threshold: f32,
305 release_threshold: f32,
307 },
308}
309
310#[derive(Component, Debug)]
339#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug))]
340#[require(GamepadSettings)]
341pub struct Gamepad {
342 pub(crate) vendor_id: Option<u16>,
344
345 pub(crate) product_id: Option<u16>,
349
350 pub(crate) digital: ButtonInput<GamepadButton>,
352
353 pub(crate) analog: Axis<GamepadInput>,
355}
356
357impl Gamepad {
358 pub fn vendor_id(&self) -> Option<u16> {
360 self.vendor_id
361 }
362
363 pub fn product_id(&self) -> Option<u16> {
367 self.product_id
368 }
369
370 pub fn get(&self, input: impl Into<GamepadInput>) -> Option<f32> {
374 self.analog.get(input.into())
375 }
376
377 pub fn get_unclamped(&self, input: impl Into<GamepadInput>) -> Option<f32> {
381 self.analog.get_unclamped(input.into())
382 }
383
384 pub fn left_stick(&self) -> Vec2 {
386 Vec2 {
387 x: self.get(GamepadAxis::LeftStickX).unwrap_or(0.0),
388 y: self.get(GamepadAxis::LeftStickY).unwrap_or(0.0),
389 }
390 }
391
392 pub fn right_stick(&self) -> Vec2 {
394 Vec2 {
395 x: self.get(GamepadAxis::RightStickX).unwrap_or(0.0),
396 y: self.get(GamepadAxis::RightStickY).unwrap_or(0.0),
397 }
398 }
399
400 pub fn dpad(&self) -> Vec2 {
402 Vec2 {
403 x: self.get(GamepadButton::DPadRight).unwrap_or(0.0)
404 - self.get(GamepadButton::DPadLeft).unwrap_or(0.0),
405 y: self.get(GamepadButton::DPadUp).unwrap_or(0.0)
406 - self.get(GamepadButton::DPadDown).unwrap_or(0.0),
407 }
408 }
409
410 pub fn pressed(&self, button_type: GamepadButton) -> bool {
412 self.digital.pressed(button_type)
413 }
414
415 pub fn any_pressed(&self, button_inputs: impl IntoIterator<Item = GamepadButton>) -> bool {
417 self.digital.any_pressed(button_inputs)
418 }
419
420 pub fn all_pressed(&self, button_inputs: impl IntoIterator<Item = GamepadButton>) -> bool {
422 self.digital.all_pressed(button_inputs)
423 }
424
425 pub fn just_pressed(&self, button_type: GamepadButton) -> bool {
429 self.digital.just_pressed(button_type)
430 }
431
432 pub fn any_just_pressed(&self, button_inputs: impl IntoIterator<Item = GamepadButton>) -> bool {
434 self.digital.any_just_pressed(button_inputs)
435 }
436
437 pub fn all_just_pressed(&self, button_inputs: impl IntoIterator<Item = GamepadButton>) -> bool {
439 self.digital.all_just_pressed(button_inputs)
440 }
441
442 pub fn just_released(&self, button_type: GamepadButton) -> bool {
446 self.digital.just_released(button_type)
447 }
448
449 pub fn any_just_released(
451 &self,
452 button_inputs: impl IntoIterator<Item = GamepadButton>,
453 ) -> bool {
454 self.digital.any_just_released(button_inputs)
455 }
456
457 pub fn all_just_released(
459 &self,
460 button_inputs: impl IntoIterator<Item = GamepadButton>,
461 ) -> bool {
462 self.digital.all_just_released(button_inputs)
463 }
464
465 pub fn get_pressed(&self) -> impl Iterator<Item = &GamepadButton> {
469 self.digital.get_pressed()
470 }
471
472 pub fn get_just_pressed(&self) -> impl Iterator<Item = &GamepadButton> {
476 self.digital.get_just_pressed()
477 }
478
479 pub fn get_just_released(&self) -> impl Iterator<Item = &GamepadButton> {
483 self.digital.get_just_released()
484 }
485
486 pub fn get_analog_axes(&self) -> impl Iterator<Item = &GamepadInput> {
490 self.analog.all_axes()
491 }
492
493 pub fn digital(&self) -> &ButtonInput<GamepadButton> {
495 &self.digital
496 }
497
498 pub fn digital_mut(&mut self) -> &mut ButtonInput<GamepadButton> {
500 &mut self.digital
501 }
502
503 pub fn analog(&self) -> &Axis<GamepadInput> {
505 &self.analog
506 }
507
508 pub fn analog_mut(&mut self) -> &mut Axis<GamepadInput> {
510 &mut self.analog
511 }
512}
513
514impl Default for Gamepad {
515 fn default() -> Self {
516 let mut analog = Axis::default();
517 for button in GamepadButton::all().iter().copied() {
518 analog.set(button, 0.0);
519 }
520 for axis_type in GamepadAxis::all().iter().copied() {
521 analog.set(axis_type, 0.0);
522 }
523
524 Self {
525 vendor_id: None,
526 product_id: None,
527 digital: Default::default(),
528 analog,
529 }
530 }
531}
532
533#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
540#[cfg_attr(
541 feature = "bevy_reflect",
542 derive(Reflect),
543 reflect(Debug, Hash, PartialEq)
544)]
545#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
546#[cfg_attr(
547 all(feature = "serialize", feature = "bevy_reflect"),
548 reflect(Serialize, Deserialize)
549)]
550pub enum GamepadButton {
551 South,
553 East,
555 North,
557 West,
559
560 C,
562 Z,
564
565 LeftTrigger,
567 LeftTrigger2,
569 RightTrigger,
571 RightTrigger2,
573 Select,
575 Start,
577 Mode,
579
580 LeftThumb,
582 RightThumb,
584
585 DPadUp,
587 DPadDown,
589 DPadLeft,
591 DPadRight,
593
594 Other(u8),
596}
597
598impl GamepadButton {
599 pub const fn all() -> [GamepadButton; 19] {
601 [
602 GamepadButton::South,
603 GamepadButton::East,
604 GamepadButton::North,
605 GamepadButton::West,
606 GamepadButton::C,
607 GamepadButton::Z,
608 GamepadButton::LeftTrigger,
609 GamepadButton::LeftTrigger2,
610 GamepadButton::RightTrigger,
611 GamepadButton::RightTrigger2,
612 GamepadButton::Select,
613 GamepadButton::Start,
614 GamepadButton::Mode,
615 GamepadButton::LeftThumb,
616 GamepadButton::RightThumb,
617 GamepadButton::DPadUp,
618 GamepadButton::DPadDown,
619 GamepadButton::DPadLeft,
620 GamepadButton::DPadRight,
621 ]
622 }
623}
624
625#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
632#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
633#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
634#[cfg_attr(
635 all(feature = "serialize", feature = "bevy_reflect"),
636 reflect(Serialize, Deserialize)
637)]
638pub enum GamepadAxis {
639 LeftStickX,
641 LeftStickY,
643 LeftZ,
645
646 RightStickX,
648 RightStickY,
650 RightZ,
652
653 Other(u8),
655}
656
657impl GamepadAxis {
658 pub const fn all() -> [GamepadAxis; 6] {
660 [
661 GamepadAxis::LeftStickX,
662 GamepadAxis::LeftStickY,
663 GamepadAxis::LeftZ,
664 GamepadAxis::RightStickX,
665 GamepadAxis::RightStickY,
666 GamepadAxis::RightZ,
667 ]
668 }
669}
670
671#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, From)]
674#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
675pub enum GamepadInput {
676 Axis(GamepadAxis),
678 Button(GamepadButton),
680}
681
682#[derive(Component, Clone, Default, Debug)]
696#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, Default))]
697pub struct GamepadSettings {
698 pub default_button_settings: ButtonSettings,
700 pub default_axis_settings: AxisSettings,
702 pub default_button_axis_settings: ButtonAxisSettings,
704 pub button_settings: HashMap<GamepadButton, ButtonSettings>,
706 pub axis_settings: HashMap<GamepadAxis, AxisSettings>,
708 pub button_axis_settings: HashMap<GamepadButton, ButtonAxisSettings>,
710}
711
712impl GamepadSettings {
713 pub fn get_button_settings(&self, button: GamepadButton) -> &ButtonSettings {
726 self.button_settings
727 .get(&button)
728 .unwrap_or(&self.default_button_settings)
729 }
730
731 pub fn get_axis_settings(&self, axis: GamepadAxis) -> &AxisSettings {
744 self.axis_settings
745 .get(&axis)
746 .unwrap_or(&self.default_axis_settings)
747 }
748
749 pub fn get_button_axis_settings(&self, button: GamepadButton) -> &ButtonAxisSettings {
762 self.button_axis_settings
763 .get(&button)
764 .unwrap_or(&self.default_button_axis_settings)
765 }
766}
767
768#[derive(Debug, PartialEq, Clone)]
776#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, Default))]
777pub struct ButtonSettings {
778 press_threshold: f32,
779 release_threshold: f32,
780}
781
782impl Default for ButtonSettings {
783 fn default() -> Self {
784 ButtonSettings {
785 press_threshold: 0.75,
786 release_threshold: 0.65,
787 }
788 }
789}
790
791impl ButtonSettings {
792 pub fn new(
809 press_threshold: f32,
810 release_threshold: f32,
811 ) -> Result<ButtonSettings, ButtonSettingsError> {
812 if !(0.0..=1.0).contains(&release_threshold) {
813 Err(ButtonSettingsError::ReleaseThresholdOutOfRange(
814 release_threshold,
815 ))
816 } else if !(0.0..=1.0).contains(&press_threshold) {
817 Err(ButtonSettingsError::PressThresholdOutOfRange(
818 press_threshold,
819 ))
820 } else if release_threshold > press_threshold {
821 Err(
822 ButtonSettingsError::ReleaseThresholdGreaterThanPressThreshold {
823 press_threshold,
824 release_threshold,
825 },
826 )
827 } else {
828 Ok(ButtonSettings {
829 press_threshold,
830 release_threshold,
831 })
832 }
833 }
834
835 pub fn is_pressed(&self, value: f32) -> bool {
839 value >= self.press_threshold
840 }
841
842 pub fn is_released(&self, value: f32) -> bool {
846 value <= self.release_threshold
847 }
848
849 pub fn press_threshold(&self) -> f32 {
851 self.press_threshold
852 }
853
854 pub fn try_set_press_threshold(&mut self, value: f32) -> Result<(), ButtonSettingsError> {
862 if (self.release_threshold..=1.0).contains(&value) {
863 self.press_threshold = value;
864 Ok(())
865 } else if !(0.0..1.0).contains(&value) {
866 Err(ButtonSettingsError::PressThresholdOutOfRange(value))
867 } else {
868 Err(
869 ButtonSettingsError::ReleaseThresholdGreaterThanPressThreshold {
870 press_threshold: value,
871 release_threshold: self.release_threshold,
872 },
873 )
874 }
875 }
876
877 pub fn set_press_threshold(&mut self, value: f32) -> f32 {
882 self.try_set_press_threshold(value).ok();
883 self.press_threshold
884 }
885
886 pub fn release_threshold(&self) -> f32 {
888 self.release_threshold
889 }
890
891 pub fn try_set_release_threshold(&mut self, value: f32) -> Result<(), ButtonSettingsError> {
899 if (0.0..=self.press_threshold).contains(&value) {
900 self.release_threshold = value;
901 Ok(())
902 } else if !(0.0..1.0).contains(&value) {
903 Err(ButtonSettingsError::ReleaseThresholdOutOfRange(value))
904 } else {
905 Err(
906 ButtonSettingsError::ReleaseThresholdGreaterThanPressThreshold {
907 press_threshold: self.press_threshold,
908 release_threshold: value,
909 },
910 )
911 }
912 }
913
914 pub fn set_release_threshold(&mut self, value: f32) -> f32 {
919 self.try_set_release_threshold(value).ok();
920 self.release_threshold
921 }
922}
923
924#[derive(Debug, Clone, PartialEq)]
936#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, Default))]
937pub struct AxisSettings {
938 livezone_upperbound: f32,
940 deadzone_upperbound: f32,
942 deadzone_lowerbound: f32,
944 livezone_lowerbound: f32,
946 threshold: f32,
948}
949
950impl Default for AxisSettings {
951 fn default() -> Self {
952 AxisSettings {
953 livezone_upperbound: 1.0,
954 deadzone_upperbound: 0.05,
955 deadzone_lowerbound: -0.05,
956 livezone_lowerbound: -1.0,
957 threshold: 0.01,
958 }
959 }
960}
961
962impl AxisSettings {
963 pub fn new(
985 livezone_lowerbound: f32,
986 deadzone_lowerbound: f32,
987 deadzone_upperbound: f32,
988 livezone_upperbound: f32,
989 threshold: f32,
990 ) -> Result<AxisSettings, AxisSettingsError> {
991 if !(-1.0..=0.0).contains(&livezone_lowerbound) {
992 Err(AxisSettingsError::LiveZoneLowerBoundOutOfRange(
993 livezone_lowerbound,
994 ))
995 } else if !(-1.0..=0.0).contains(&deadzone_lowerbound) {
996 Err(AxisSettingsError::DeadZoneLowerBoundOutOfRange(
997 deadzone_lowerbound,
998 ))
999 } else if !(0.0..=1.0).contains(&deadzone_upperbound) {
1000 Err(AxisSettingsError::DeadZoneUpperBoundOutOfRange(
1001 deadzone_upperbound,
1002 ))
1003 } else if !(0.0..=1.0).contains(&livezone_upperbound) {
1004 Err(AxisSettingsError::LiveZoneUpperBoundOutOfRange(
1005 livezone_upperbound,
1006 ))
1007 } else if livezone_lowerbound > deadzone_lowerbound {
1008 Err(
1009 AxisSettingsError::LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
1010 livezone_lowerbound,
1011 deadzone_lowerbound,
1012 },
1013 )
1014 } else if deadzone_upperbound > livezone_upperbound {
1015 Err(
1016 AxisSettingsError::DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
1017 livezone_upperbound,
1018 deadzone_upperbound,
1019 },
1020 )
1021 } else if !(0.0..=2.0).contains(&threshold) {
1022 Err(AxisSettingsError::Threshold(threshold))
1023 } else {
1024 Ok(Self {
1025 livezone_lowerbound,
1026 deadzone_lowerbound,
1027 deadzone_upperbound,
1028 livezone_upperbound,
1029 threshold,
1030 })
1031 }
1032 }
1033
1034 pub fn livezone_upperbound(&self) -> f32 {
1036 self.livezone_upperbound
1037 }
1038
1039 pub fn try_set_livezone_upperbound(&mut self, value: f32) -> Result<(), AxisSettingsError> {
1047 if !(0.0..=1.0).contains(&value) {
1048 Err(AxisSettingsError::LiveZoneUpperBoundOutOfRange(value))
1049 } else if value < self.deadzone_upperbound {
1050 Err(
1051 AxisSettingsError::DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
1052 livezone_upperbound: value,
1053 deadzone_upperbound: self.deadzone_upperbound,
1054 },
1055 )
1056 } else {
1057 self.livezone_upperbound = value;
1058 Ok(())
1059 }
1060 }
1061
1062 pub fn set_livezone_upperbound(&mut self, value: f32) -> f32 {
1068 self.try_set_livezone_upperbound(value).ok();
1069 self.livezone_upperbound
1070 }
1071
1072 pub fn deadzone_upperbound(&self) -> f32 {
1074 self.deadzone_upperbound
1075 }
1076
1077 pub fn try_set_deadzone_upperbound(&mut self, value: f32) -> Result<(), AxisSettingsError> {
1085 if !(0.0..=1.0).contains(&value) {
1086 Err(AxisSettingsError::DeadZoneUpperBoundOutOfRange(value))
1087 } else if self.livezone_upperbound < value {
1088 Err(
1089 AxisSettingsError::DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
1090 livezone_upperbound: self.livezone_upperbound,
1091 deadzone_upperbound: value,
1092 },
1093 )
1094 } else {
1095 self.deadzone_upperbound = value;
1096 Ok(())
1097 }
1098 }
1099
1100 pub fn set_deadzone_upperbound(&mut self, value: f32) -> f32 {
1106 self.try_set_deadzone_upperbound(value).ok();
1107 self.deadzone_upperbound
1108 }
1109
1110 pub fn livezone_lowerbound(&self) -> f32 {
1112 self.livezone_lowerbound
1113 }
1114
1115 pub fn try_set_livezone_lowerbound(&mut self, value: f32) -> Result<(), AxisSettingsError> {
1123 if !(-1.0..=0.0).contains(&value) {
1124 Err(AxisSettingsError::LiveZoneLowerBoundOutOfRange(value))
1125 } else if value > self.deadzone_lowerbound {
1126 Err(
1127 AxisSettingsError::LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
1128 livezone_lowerbound: value,
1129 deadzone_lowerbound: self.deadzone_lowerbound,
1130 },
1131 )
1132 } else {
1133 self.livezone_lowerbound = value;
1134 Ok(())
1135 }
1136 }
1137
1138 pub fn set_livezone_lowerbound(&mut self, value: f32) -> f32 {
1144 self.try_set_livezone_lowerbound(value).ok();
1145 self.livezone_lowerbound
1146 }
1147
1148 pub fn deadzone_lowerbound(&self) -> f32 {
1150 self.deadzone_lowerbound
1151 }
1152
1153 pub fn try_set_deadzone_lowerbound(&mut self, value: f32) -> Result<(), AxisSettingsError> {
1161 if !(-1.0..=0.0).contains(&value) {
1162 Err(AxisSettingsError::DeadZoneLowerBoundOutOfRange(value))
1163 } else if self.livezone_lowerbound > value {
1164 Err(
1165 AxisSettingsError::LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
1166 livezone_lowerbound: self.livezone_lowerbound,
1167 deadzone_lowerbound: value,
1168 },
1169 )
1170 } else {
1171 self.deadzone_lowerbound = value;
1172 Ok(())
1173 }
1174 }
1175
1176 pub fn set_deadzone_lowerbound(&mut self, value: f32) -> f32 {
1182 self.try_set_deadzone_lowerbound(value).ok();
1183 self.deadzone_lowerbound
1184 }
1185
1186 pub fn threshold(&self) -> f32 {
1188 self.threshold
1189 }
1190
1191 pub fn try_set_threshold(&mut self, value: f32) -> Result<(), AxisSettingsError> {
1197 if !(0.0..=2.0).contains(&value) {
1198 Err(AxisSettingsError::Threshold(value))
1199 } else {
1200 self.threshold = value;
1201 Ok(())
1202 }
1203 }
1204
1205 pub fn set_threshold(&mut self, value: f32) -> f32 {
1210 self.try_set_threshold(value).ok();
1211 self.threshold
1212 }
1213
1214 pub fn clamp(&self, new_value: f32) -> f32 {
1216 if self.deadzone_lowerbound <= new_value && new_value <= self.deadzone_upperbound {
1217 0.0
1218 } else if new_value >= self.livezone_upperbound {
1219 1.0
1220 } else if new_value <= self.livezone_lowerbound {
1221 -1.0
1222 } else {
1223 new_value
1224 }
1225 }
1226
1227 fn should_register_change(&self, new_value: f32, old_value: Option<f32>) -> bool {
1230 if old_value.is_none() {
1231 return true;
1232 }
1233
1234 f32::abs(new_value - old_value.unwrap()) > self.threshold
1235 }
1236
1237 pub fn filter(&self, new_value: f32, old_value: Option<f32>) -> Option<f32> {
1242 let new_value = self.clamp(new_value);
1243
1244 if self.should_register_change(new_value, old_value) {
1245 return Some(new_value);
1246 }
1247 None
1248 }
1249}
1250
1251#[derive(Debug, Clone)]
1264#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, Default))]
1265pub struct ButtonAxisSettings {
1266 pub high: f32,
1268 pub low: f32,
1270 pub threshold: f32,
1272}
1273
1274impl Default for ButtonAxisSettings {
1275 fn default() -> Self {
1276 ButtonAxisSettings {
1277 high: 0.95,
1278 low: 0.05,
1279 threshold: 0.01,
1280 }
1281 }
1282}
1283
1284impl ButtonAxisSettings {
1285 fn clamp(&self, raw_value: f32) -> f32 {
1292 if raw_value <= self.low {
1293 return 0.0;
1294 }
1295 if raw_value >= self.high {
1296 return 1.0;
1297 }
1298
1299 raw_value
1300 }
1301
1302 fn should_register_change(&self, new_value: f32, old_value: Option<f32>) -> bool {
1305 if old_value.is_none() {
1306 return true;
1307 }
1308
1309 f32::abs(new_value - old_value.unwrap()) > self.threshold
1310 }
1311
1312 pub fn filter(&self, new_value: f32, old_value: Option<f32>) -> Option<f32> {
1317 let new_value = self.clamp(new_value);
1318
1319 if self.should_register_change(new_value, old_value) {
1320 return Some(new_value);
1321 }
1322 None
1323 }
1324}
1325
1326pub fn gamepad_connection_system(
1336 mut commands: Commands,
1337 mut connection_events: EventReader<GamepadConnectionEvent>,
1338) {
1339 for connection_event in connection_events.read() {
1340 let id = connection_event.gamepad;
1341 match &connection_event.connection {
1342 GamepadConnection::Connected {
1343 name,
1344 vendor_id,
1345 product_id,
1346 } => {
1347 let Some(mut gamepad) = commands.get_entity(id) else {
1348 warn!("Gamepad {:} removed before handling connection event.", id);
1349 continue;
1350 };
1351 gamepad.insert((
1352 Name::new(name.clone()),
1353 Gamepad {
1354 vendor_id: *vendor_id,
1355 product_id: *product_id,
1356 ..Default::default()
1357 },
1358 ));
1359 info!("Gamepad {:?} connected.", id);
1360 }
1361 GamepadConnection::Disconnected => {
1362 let Some(mut gamepad) = commands.get_entity(id) else {
1363 warn!("Gamepad {:} removed before handling disconnection event. You can ignore this if you manually removed it.", id);
1364 continue;
1365 };
1366 gamepad.remove::<Gamepad>();
1370 info!("Gamepad {:} disconnected.", id);
1371 }
1372 }
1373 }
1374}
1375
1376#[derive(Debug, Clone, PartialEq)]
1381#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
1382#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1383#[cfg_attr(
1384 all(feature = "serialize", feature = "bevy_reflect"),
1385 reflect(Serialize, Deserialize)
1386)]
1387pub enum GamepadConnection {
1388 Connected {
1390 name: String,
1396
1397 vendor_id: Option<u16>,
1399
1400 product_id: Option<u16>,
1402 },
1403 Disconnected,
1405}
1406
1407pub fn gamepad_event_processing_system(
1410 mut gamepads: Query<(&mut Gamepad, &GamepadSettings)>,
1411 mut raw_events: EventReader<RawGamepadEvent>,
1412 mut processed_events: EventWriter<GamepadEvent>,
1413 mut processed_axis_events: EventWriter<GamepadAxisChangedEvent>,
1414 mut processed_digital_events: EventWriter<GamepadButtonStateChangedEvent>,
1415 mut processed_analog_events: EventWriter<GamepadButtonChangedEvent>,
1416) {
1417 for (mut gamepad, _) in gamepads.iter_mut() {
1419 gamepad.bypass_change_detection().digital.clear();
1420 }
1421
1422 for event in raw_events.read() {
1423 match event {
1424 RawGamepadEvent::Connection(send_event) => {
1426 processed_events.send(GamepadEvent::from(send_event.clone()));
1427 }
1428 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent {
1429 gamepad,
1430 axis,
1431 value,
1432 }) => {
1433 let (gamepad, axis, value) = (*gamepad, *axis, *value);
1434 let Ok((mut gamepad_axis, gamepad_settings)) = gamepads.get_mut(gamepad) else {
1435 continue;
1436 };
1437 let Some(filtered_value) = gamepad_settings
1438 .get_axis_settings(axis)
1439 .filter(value, gamepad_axis.get(axis))
1440 else {
1441 continue;
1442 };
1443
1444 gamepad_axis.analog.set(axis, filtered_value);
1445 let send_event = GamepadAxisChangedEvent::new(gamepad, axis, filtered_value);
1446 processed_axis_events.send(send_event);
1447 processed_events.send(GamepadEvent::from(send_event));
1448 }
1449 RawGamepadEvent::Button(RawGamepadButtonChangedEvent {
1450 gamepad,
1451 button,
1452 value,
1453 }) => {
1454 let (gamepad, button, value) = (*gamepad, *button, *value);
1455 let Ok((mut gamepad_buttons, settings)) = gamepads.get_mut(gamepad) else {
1456 continue;
1457 };
1458 let Some(filtered_value) = settings
1459 .get_button_axis_settings(button)
1460 .filter(value, gamepad_buttons.get(button))
1461 else {
1462 continue;
1463 };
1464 let button_settings = settings.get_button_settings(button);
1465 gamepad_buttons.analog.set(button, filtered_value);
1466
1467 if button_settings.is_released(filtered_value) {
1468 if gamepad_buttons.pressed(button) {
1470 processed_digital_events.send(GamepadButtonStateChangedEvent::new(
1471 gamepad,
1472 button,
1473 ButtonState::Released,
1474 ));
1475 }
1476 gamepad_buttons.digital.release(button);
1479 } else if button_settings.is_pressed(filtered_value) {
1480 if !gamepad_buttons.pressed(button) {
1482 processed_digital_events.send(GamepadButtonStateChangedEvent::new(
1483 gamepad,
1484 button,
1485 ButtonState::Pressed,
1486 ));
1487 }
1488 gamepad_buttons.digital.press(button);
1489 };
1490
1491 let button_state = if gamepad_buttons.digital.pressed(button) {
1492 ButtonState::Pressed
1493 } else {
1494 ButtonState::Released
1495 };
1496 let send_event =
1497 GamepadButtonChangedEvent::new(gamepad, button, button_state, filtered_value);
1498 processed_analog_events.send(send_event);
1499 processed_events.send(GamepadEvent::from(send_event));
1500 }
1501 }
1502 }
1503}
1504
1505#[derive(Clone, Copy, Debug, PartialEq)]
1507#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
1508pub struct GamepadRumbleIntensity {
1509 pub strong_motor: f32,
1516 pub weak_motor: f32,
1523}
1524
1525impl GamepadRumbleIntensity {
1526 pub const MAX: Self = GamepadRumbleIntensity {
1528 strong_motor: 1.0,
1529 weak_motor: 1.0,
1530 };
1531
1532 pub const WEAK_MAX: Self = GamepadRumbleIntensity {
1534 strong_motor: 0.0,
1535 weak_motor: 1.0,
1536 };
1537
1538 pub const STRONG_MAX: Self = GamepadRumbleIntensity {
1540 strong_motor: 1.0,
1541 weak_motor: 0.0,
1542 };
1543
1544 pub const fn weak_motor(intensity: f32) -> Self {
1548 Self {
1549 weak_motor: intensity,
1550 strong_motor: 0.0,
1551 }
1552 }
1553
1554 pub const fn strong_motor(intensity: f32) -> Self {
1558 Self {
1559 strong_motor: intensity,
1560 weak_motor: 0.0,
1561 }
1562 }
1563}
1564
1565#[doc(alias = "haptic feedback")]
1591#[doc(alias = "force feedback")]
1592#[doc(alias = "vibration")]
1593#[doc(alias = "vibrate")]
1594#[derive(Event, Clone)]
1595#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
1596pub enum GamepadRumbleRequest {
1597 Add {
1608 duration: Duration,
1610 intensity: GamepadRumbleIntensity,
1612 gamepad: Entity,
1614 },
1615 Stop {
1617 gamepad: Entity,
1619 },
1620}
1621
1622impl GamepadRumbleRequest {
1623 pub fn gamepad(&self) -> Entity {
1625 match self {
1626 Self::Add { gamepad, .. } | Self::Stop { gamepad } => *gamepad,
1627 }
1628 }
1629}
1630
1631#[cfg(test)]
1632mod tests {
1633 use super::{
1634 gamepad_connection_system, gamepad_event_processing_system, AxisSettings,
1635 AxisSettingsError, ButtonAxisSettings, ButtonSettings, ButtonSettingsError, Gamepad,
1636 GamepadAxis, GamepadAxisChangedEvent, GamepadButton, GamepadButtonChangedEvent,
1637 GamepadButtonStateChangedEvent,
1638 GamepadConnection::{Connected, Disconnected},
1639 GamepadConnectionEvent, GamepadEvent, GamepadSettings, RawGamepadAxisChangedEvent,
1640 RawGamepadButtonChangedEvent, RawGamepadEvent,
1641 };
1642 use crate::ButtonState;
1643 use bevy_app::{App, PreUpdate};
1644 use bevy_ecs::entity::Entity;
1645 use bevy_ecs::event::Events;
1646 use bevy_ecs::schedule::IntoSystemConfigs;
1647
1648 fn test_button_axis_settings_filter(
1649 settings: ButtonAxisSettings,
1650 new_value: f32,
1651 old_value: Option<f32>,
1652 expected: Option<f32>,
1653 ) {
1654 let actual = settings.filter(new_value, old_value);
1655 assert_eq!(
1656 expected, actual,
1657 "Testing filtering for {settings:?} with new_value = {new_value:?}, old_value = {old_value:?}",
1658 );
1659 }
1660
1661 #[test]
1662 fn test_button_axis_settings_default_filter() {
1663 let cases = [
1664 (1.0, None, Some(1.0)),
1665 (0.99, None, Some(1.0)),
1666 (0.96, None, Some(1.0)),
1667 (0.95, None, Some(1.0)),
1668 (0.9499, None, Some(0.9499)),
1669 (0.84, None, Some(0.84)),
1670 (0.43, None, Some(0.43)),
1671 (0.05001, None, Some(0.05001)),
1672 (0.05, None, Some(0.0)),
1673 (0.04, None, Some(0.0)),
1674 (0.01, None, Some(0.0)),
1675 (0.0, None, Some(0.0)),
1676 ];
1677
1678 for (new_value, old_value, expected) in cases {
1679 let settings = ButtonAxisSettings::default();
1680 test_button_axis_settings_filter(settings, new_value, old_value, expected);
1681 }
1682 }
1683
1684 #[test]
1685 fn test_button_axis_settings_default_filter_with_old_value() {
1686 let cases = [
1687 (0.43, Some(0.44001), Some(0.43)),
1688 (0.43, Some(0.44), None),
1689 (0.43, Some(0.43), None),
1690 (0.43, Some(0.41999), Some(0.43)),
1691 (0.43, Some(0.17), Some(0.43)),
1692 (0.43, Some(0.84), Some(0.43)),
1693 (0.05, Some(0.055), Some(0.0)),
1694 (0.95, Some(0.945), Some(1.0)),
1695 ];
1696
1697 for (new_value, old_value, expected) in cases {
1698 let settings = ButtonAxisSettings::default();
1699 test_button_axis_settings_filter(settings, new_value, old_value, expected);
1700 }
1701 }
1702
1703 fn test_axis_settings_filter(
1704 settings: AxisSettings,
1705 new_value: f32,
1706 old_value: Option<f32>,
1707 expected: Option<f32>,
1708 ) {
1709 let actual = settings.filter(new_value, old_value);
1710 assert_eq!(
1711 expected, actual,
1712 "Testing filtering for {settings:?} with new_value = {new_value:?}, old_value = {old_value:?}",
1713 );
1714 }
1715
1716 #[test]
1717 fn test_axis_settings_default_filter() {
1718 let cases = [
1719 (1.0, Some(1.0)),
1720 (0.99, Some(1.0)),
1721 (0.96, Some(1.0)),
1722 (0.95, Some(1.0)),
1723 (0.9499, Some(0.9499)),
1724 (0.84, Some(0.84)),
1725 (0.43, Some(0.43)),
1726 (0.05001, Some(0.05001)),
1727 (0.05, Some(0.0)),
1728 (0.04, Some(0.0)),
1729 (0.01, Some(0.0)),
1730 (0.0, Some(0.0)),
1731 (-1.0, Some(-1.0)),
1732 (-0.99, Some(-1.0)),
1733 (-0.96, Some(-1.0)),
1734 (-0.95, Some(-1.0)),
1735 (-0.9499, Some(-0.9499)),
1736 (-0.84, Some(-0.84)),
1737 (-0.43, Some(-0.43)),
1738 (-0.05001, Some(-0.05001)),
1739 (-0.05, Some(0.0)),
1740 (-0.04, Some(0.0)),
1741 (-0.01, Some(0.0)),
1742 ];
1743
1744 for (new_value, expected) in cases {
1745 let settings = AxisSettings::new(-0.95, -0.05, 0.05, 0.95, 0.01).unwrap();
1746 test_axis_settings_filter(settings, new_value, None, expected);
1747 }
1748 }
1749
1750 #[test]
1751 fn test_axis_settings_default_filter_with_old_values() {
1752 let cases = [
1753 (0.43, Some(0.44001), Some(0.43)),
1754 (0.43, Some(0.44), None),
1755 (0.43, Some(0.43), None),
1756 (0.43, Some(0.41999), Some(0.43)),
1757 (0.43, Some(0.17), Some(0.43)),
1758 (0.43, Some(0.84), Some(0.43)),
1759 (0.05, Some(0.055), Some(0.0)),
1760 (0.95, Some(0.945), Some(1.0)),
1761 (-0.43, Some(-0.44001), Some(-0.43)),
1762 (-0.43, Some(-0.44), None),
1763 (-0.43, Some(-0.43), None),
1764 (-0.43, Some(-0.41999), Some(-0.43)),
1765 (-0.43, Some(-0.17), Some(-0.43)),
1766 (-0.43, Some(-0.84), Some(-0.43)),
1767 (-0.05, Some(-0.055), Some(0.0)),
1768 (-0.95, Some(-0.945), Some(-1.0)),
1769 ];
1770
1771 for (new_value, old_value, expected) in cases {
1772 let settings = AxisSettings::new(-0.95, -0.05, 0.05, 0.95, 0.01).unwrap();
1773 test_axis_settings_filter(settings, new_value, old_value, expected);
1774 }
1775 }
1776
1777 #[test]
1778 fn test_button_settings_default_is_pressed() {
1779 let cases = [
1780 (1.0, true),
1781 (0.95, true),
1782 (0.9, true),
1783 (0.8, true),
1784 (0.75, true),
1785 (0.7, false),
1786 (0.65, false),
1787 (0.5, false),
1788 (0.0, false),
1789 ];
1790
1791 for (value, expected) in cases {
1792 let settings = ButtonSettings::default();
1793 let actual = settings.is_pressed(value);
1794
1795 assert_eq!(
1796 expected, actual,
1797 "testing ButtonSettings::is_pressed() for value: {value}",
1798 );
1799 }
1800 }
1801
1802 #[test]
1803 fn test_button_settings_default_is_released() {
1804 let cases = [
1805 (1.0, false),
1806 (0.95, false),
1807 (0.9, false),
1808 (0.8, false),
1809 (0.75, false),
1810 (0.7, false),
1811 (0.65, true),
1812 (0.5, true),
1813 (0.0, true),
1814 ];
1815
1816 for (value, expected) in cases {
1817 let settings = ButtonSettings::default();
1818 let actual = settings.is_released(value);
1819
1820 assert_eq!(
1821 expected, actual,
1822 "testing ButtonSettings::is_released() for value: {value}",
1823 );
1824 }
1825 }
1826
1827 #[test]
1828 fn test_new_button_settings_given_valid_parameters() {
1829 let cases = [
1830 (1.0, 0.0),
1831 (1.0, 1.0),
1832 (1.0, 0.9),
1833 (0.9, 0.9),
1834 (0.9, 0.0),
1835 (0.0, 0.0),
1836 ];
1837
1838 for (press_threshold, release_threshold) in cases {
1839 let bs = ButtonSettings::new(press_threshold, release_threshold);
1840 match bs {
1841 Ok(button_settings) => {
1842 assert_eq!(button_settings.press_threshold, press_threshold);
1843 assert_eq!(button_settings.release_threshold, release_threshold);
1844 }
1845 Err(_) => {
1846 panic!(
1847 "ButtonSettings::new({press_threshold}, {release_threshold}) should be valid"
1848 );
1849 }
1850 }
1851 }
1852 }
1853
1854 #[test]
1855 fn test_new_button_settings_given_invalid_parameters() {
1856 let cases = [
1857 (1.1, 0.0),
1858 (1.1, 1.0),
1859 (1.0, 1.1),
1860 (-1.0, 0.9),
1861 (-1.0, 0.0),
1862 (-1.0, -0.4),
1863 (0.9, 1.0),
1864 (0.0, 0.1),
1865 ];
1866
1867 for (press_threshold, release_threshold) in cases {
1868 let bs = ButtonSettings::new(press_threshold, release_threshold);
1869 match bs {
1870 Ok(_) => {
1871 panic!(
1872 "ButtonSettings::new({press_threshold}, {release_threshold}) should be invalid"
1873 );
1874 }
1875 Err(err_code) => match err_code {
1876 ButtonSettingsError::PressThresholdOutOfRange(_press_threshold) => {}
1877 ButtonSettingsError::ReleaseThresholdGreaterThanPressThreshold {
1878 press_threshold: _press_threshold,
1879 release_threshold: _release_threshold,
1880 } => {}
1881 ButtonSettingsError::ReleaseThresholdOutOfRange(_release_threshold) => {}
1882 },
1883 }
1884 }
1885 }
1886
1887 #[test]
1888 fn test_try_out_of_range_axis_settings() {
1889 let mut axis_settings = AxisSettings::default();
1890 assert_eq!(
1891 AxisSettings::new(-0.95, -0.05, 0.05, 0.95, 0.001),
1892 Ok(AxisSettings {
1893 livezone_lowerbound: -0.95,
1894 deadzone_lowerbound: -0.05,
1895 deadzone_upperbound: 0.05,
1896 livezone_upperbound: 0.95,
1897 threshold: 0.001,
1898 })
1899 );
1900 assert_eq!(
1901 Err(AxisSettingsError::LiveZoneLowerBoundOutOfRange(-2.0)),
1902 axis_settings.try_set_livezone_lowerbound(-2.0)
1903 );
1904 assert_eq!(
1905 Err(AxisSettingsError::LiveZoneLowerBoundOutOfRange(0.1)),
1906 axis_settings.try_set_livezone_lowerbound(0.1)
1907 );
1908 assert_eq!(
1909 Err(AxisSettingsError::DeadZoneLowerBoundOutOfRange(-2.0)),
1910 axis_settings.try_set_deadzone_lowerbound(-2.0)
1911 );
1912 assert_eq!(
1913 Err(AxisSettingsError::DeadZoneLowerBoundOutOfRange(0.1)),
1914 axis_settings.try_set_deadzone_lowerbound(0.1)
1915 );
1916
1917 assert_eq!(
1918 Err(AxisSettingsError::DeadZoneUpperBoundOutOfRange(-0.1)),
1919 axis_settings.try_set_deadzone_upperbound(-0.1)
1920 );
1921 assert_eq!(
1922 Err(AxisSettingsError::DeadZoneUpperBoundOutOfRange(1.1)),
1923 axis_settings.try_set_deadzone_upperbound(1.1)
1924 );
1925 assert_eq!(
1926 Err(AxisSettingsError::LiveZoneUpperBoundOutOfRange(-0.1)),
1927 axis_settings.try_set_livezone_upperbound(-0.1)
1928 );
1929 assert_eq!(
1930 Err(AxisSettingsError::LiveZoneUpperBoundOutOfRange(1.1)),
1931 axis_settings.try_set_livezone_upperbound(1.1)
1932 );
1933
1934 axis_settings.set_livezone_lowerbound(-0.7);
1935 axis_settings.set_deadzone_lowerbound(-0.3);
1936 assert_eq!(
1937 Err(
1938 AxisSettingsError::LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
1939 livezone_lowerbound: -0.1,
1940 deadzone_lowerbound: -0.3,
1941 }
1942 ),
1943 axis_settings.try_set_livezone_lowerbound(-0.1)
1944 );
1945 assert_eq!(
1946 Err(
1947 AxisSettingsError::LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
1948 livezone_lowerbound: -0.7,
1949 deadzone_lowerbound: -0.9
1950 }
1951 ),
1952 axis_settings.try_set_deadzone_lowerbound(-0.9)
1953 );
1954
1955 axis_settings.set_deadzone_upperbound(0.3);
1956 axis_settings.set_livezone_upperbound(0.7);
1957 assert_eq!(
1958 Err(
1959 AxisSettingsError::DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
1960 deadzone_upperbound: 0.8,
1961 livezone_upperbound: 0.7
1962 }
1963 ),
1964 axis_settings.try_set_deadzone_upperbound(0.8)
1965 );
1966 assert_eq!(
1967 Err(
1968 AxisSettingsError::DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
1969 deadzone_upperbound: 0.3,
1970 livezone_upperbound: 0.1
1971 }
1972 ),
1973 axis_settings.try_set_livezone_upperbound(0.1)
1974 );
1975 }
1976
1977 struct TestContext {
1978 pub app: App,
1979 }
1980
1981 impl TestContext {
1982 pub fn new() -> Self {
1983 let mut app = App::new();
1984 app.add_systems(
1985 PreUpdate,
1986 (
1987 gamepad_connection_system,
1988 gamepad_event_processing_system.after(gamepad_connection_system),
1989 ),
1990 )
1991 .add_event::<GamepadEvent>()
1992 .add_event::<GamepadConnectionEvent>()
1993 .add_event::<RawGamepadButtonChangedEvent>()
1994 .add_event::<GamepadButtonChangedEvent>()
1995 .add_event::<GamepadButtonStateChangedEvent>()
1996 .add_event::<GamepadAxisChangedEvent>()
1997 .add_event::<RawGamepadAxisChangedEvent>()
1998 .add_event::<RawGamepadEvent>();
1999 Self { app }
2000 }
2001
2002 pub fn update(&mut self) {
2003 self.app.update();
2004 }
2005
2006 pub fn send_gamepad_connection_event(&mut self, gamepad: Option<Entity>) -> Entity {
2007 let gamepad = gamepad.unwrap_or_else(|| self.app.world_mut().spawn_empty().id());
2008 self.app
2009 .world_mut()
2010 .resource_mut::<Events<GamepadConnectionEvent>>()
2011 .send(GamepadConnectionEvent::new(
2012 gamepad,
2013 Connected {
2014 name: "Test gamepad".to_string(),
2015 vendor_id: None,
2016 product_id: None,
2017 },
2018 ));
2019 gamepad
2020 }
2021
2022 pub fn send_gamepad_disconnection_event(&mut self, gamepad: Entity) {
2023 self.app
2024 .world_mut()
2025 .resource_mut::<Events<GamepadConnectionEvent>>()
2026 .send(GamepadConnectionEvent::new(gamepad, Disconnected));
2027 }
2028
2029 pub fn send_raw_gamepad_event(&mut self, event: RawGamepadEvent) {
2030 self.app
2031 .world_mut()
2032 .resource_mut::<Events<RawGamepadEvent>>()
2033 .send(event);
2034 }
2035
2036 pub fn send_raw_gamepad_event_batch(
2037 &mut self,
2038 events: impl IntoIterator<Item = RawGamepadEvent>,
2039 ) {
2040 self.app
2041 .world_mut()
2042 .resource_mut::<Events<RawGamepadEvent>>()
2043 .send_batch(events);
2044 }
2045 }
2046
2047 #[test]
2048 fn connection_event() {
2049 let mut ctx = TestContext::new();
2050 assert_eq!(
2051 ctx.app
2052 .world_mut()
2053 .query::<&Gamepad>()
2054 .iter(ctx.app.world())
2055 .len(),
2056 0
2057 );
2058 ctx.send_gamepad_connection_event(None);
2059 ctx.update();
2060 assert_eq!(
2061 ctx.app
2062 .world_mut()
2063 .query::<(&Gamepad, &GamepadSettings)>()
2064 .iter(ctx.app.world())
2065 .len(),
2066 1
2067 );
2068 }
2069
2070 #[test]
2071 fn disconnection_event() {
2072 let mut ctx = TestContext::new();
2073 assert_eq!(
2074 ctx.app
2075 .world_mut()
2076 .query::<&Gamepad>()
2077 .iter(ctx.app.world())
2078 .len(),
2079 0
2080 );
2081 let entity = ctx.send_gamepad_connection_event(None);
2082 ctx.update();
2083 assert_eq!(
2084 ctx.app
2085 .world_mut()
2086 .query::<(&Gamepad, &GamepadSettings)>()
2087 .iter(ctx.app.world())
2088 .len(),
2089 1
2090 );
2091 ctx.send_gamepad_disconnection_event(entity);
2092 ctx.update();
2093 assert!(ctx
2095 .app
2096 .world_mut()
2097 .query::<&Gamepad>()
2098 .get(ctx.app.world(), entity)
2099 .is_err());
2100 assert!(ctx
2102 .app
2103 .world_mut()
2104 .query::<&GamepadSettings>()
2105 .get(ctx.app.world(), entity)
2106 .is_ok());
2107
2108 ctx.send_gamepad_disconnection_event(entity);
2110 ctx.update();
2111 assert!(ctx
2112 .app
2113 .world_mut()
2114 .query::<&Gamepad>()
2115 .get(ctx.app.world(), entity)
2116 .is_err());
2117 assert!(ctx
2118 .app
2119 .world_mut()
2120 .query::<&GamepadSettings>()
2121 .get(ctx.app.world(), entity)
2122 .is_ok());
2123 }
2124
2125 #[test]
2126 fn connection_disconnection_frame_event() {
2127 let mut ctx = TestContext::new();
2128 assert_eq!(
2129 ctx.app
2130 .world_mut()
2131 .query::<&Gamepad>()
2132 .iter(ctx.app.world())
2133 .len(),
2134 0
2135 );
2136 let entity = ctx.send_gamepad_connection_event(None);
2137 ctx.send_gamepad_disconnection_event(entity);
2138 ctx.update();
2139 assert!(ctx
2141 .app
2142 .world_mut()
2143 .query::<&Gamepad>()
2144 .get(ctx.app.world(), entity)
2145 .is_err());
2146 assert!(ctx
2148 .app
2149 .world_mut()
2150 .query::<&GamepadSettings>()
2151 .get(ctx.app.world(), entity)
2152 .is_ok());
2153 }
2154
2155 #[test]
2156 fn reconnection_event() {
2157 let button_settings = ButtonSettings::new(0.7, 0.2).expect("correct parameters");
2158 let mut ctx = TestContext::new();
2159 assert_eq!(
2160 ctx.app
2161 .world_mut()
2162 .query::<&Gamepad>()
2163 .iter(ctx.app.world())
2164 .len(),
2165 0
2166 );
2167 let entity = ctx.send_gamepad_connection_event(None);
2168 ctx.update();
2169 let mut settings = ctx
2170 .app
2171 .world_mut()
2172 .query::<&mut GamepadSettings>()
2173 .get_mut(ctx.app.world_mut(), entity)
2174 .expect("be alive");
2175 assert_ne!(settings.default_button_settings, button_settings);
2176 settings.default_button_settings = button_settings.clone();
2177 ctx.send_gamepad_disconnection_event(entity);
2178 ctx.update();
2179 assert_eq!(
2180 ctx.app
2181 .world_mut()
2182 .query::<&Gamepad>()
2183 .iter(ctx.app.world())
2184 .len(),
2185 0
2186 );
2187 ctx.send_gamepad_connection_event(Some(entity));
2188 ctx.update();
2189 let settings = ctx
2190 .app
2191 .world_mut()
2192 .query::<&GamepadSettings>()
2193 .get(ctx.app.world(), entity)
2194 .expect("be alive");
2195 assert_eq!(settings.default_button_settings, button_settings);
2196 }
2197
2198 #[test]
2199 fn reconnection_same_frame_event() {
2200 let mut ctx = TestContext::new();
2201 assert_eq!(
2202 ctx.app
2203 .world_mut()
2204 .query::<&Gamepad>()
2205 .iter(ctx.app.world())
2206 .len(),
2207 0
2208 );
2209 let entity = ctx.send_gamepad_connection_event(None);
2210 ctx.send_gamepad_disconnection_event(entity);
2211 ctx.update();
2212 assert_eq!(
2213 ctx.app
2214 .world_mut()
2215 .query::<&Gamepad>()
2216 .iter(ctx.app.world())
2217 .len(),
2218 0
2219 );
2220 assert!(ctx
2221 .app
2222 .world_mut()
2223 .query::<(Entity, &GamepadSettings)>()
2224 .get(ctx.app.world(), entity)
2225 .is_ok());
2226 }
2227
2228 #[test]
2229 fn gamepad_axis_valid() {
2230 let mut ctx = TestContext::new();
2231
2232 let entity = ctx.send_gamepad_connection_event(None);
2234 ctx.app
2235 .world_mut()
2236 .resource_mut::<Events<RawGamepadEvent>>()
2237 .send_batch([
2238 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2239 entity,
2240 GamepadAxis::LeftStickY,
2241 0.5,
2242 )),
2243 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2244 entity,
2245 GamepadAxis::RightStickX,
2246 0.6,
2247 )),
2248 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2249 entity,
2250 GamepadAxis::RightZ,
2251 -0.4,
2252 )),
2253 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2254 entity,
2255 GamepadAxis::RightStickY,
2256 -0.8,
2257 )),
2258 ]);
2259 ctx.update();
2260 assert_eq!(
2261 ctx.app
2262 .world()
2263 .resource::<Events<GamepadAxisChangedEvent>>()
2264 .len(),
2265 4
2266 );
2267 }
2268
2269 #[test]
2270 fn gamepad_axis_threshold_filter() {
2271 let mut ctx = TestContext::new();
2272
2273 let entity = ctx.send_gamepad_connection_event(None);
2275 let settings = GamepadSettings::default().default_axis_settings;
2276 let base_value = 0.5;
2278 let events = [
2279 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2281 entity,
2282 GamepadAxis::LeftStickX,
2283 base_value,
2284 )),
2285 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2287 entity,
2288 GamepadAxis::LeftStickX,
2289 base_value + settings.threshold - 0.01,
2290 )),
2291 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2293 entity,
2294 GamepadAxis::LeftStickX,
2295 base_value + settings.threshold + 0.01,
2296 )),
2297 ];
2298 ctx.app
2299 .world_mut()
2300 .resource_mut::<Events<RawGamepadEvent>>()
2301 .send_batch(events);
2302 ctx.update();
2303 assert_eq!(
2304 ctx.app
2305 .world()
2306 .resource::<Events<GamepadAxisChangedEvent>>()
2307 .len(),
2308 2
2309 );
2310 }
2311
2312 #[test]
2313 fn gamepad_axis_deadzone_filter() {
2314 let mut ctx = TestContext::new();
2315
2316 let entity = ctx.send_gamepad_connection_event(None);
2318 let settings = GamepadSettings::default().default_axis_settings;
2319
2320 let events = [
2322 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2324 entity,
2325 GamepadAxis::LeftStickX,
2326 settings.deadzone_upperbound - 0.01,
2327 )),
2328 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2330 entity,
2331 GamepadAxis::LeftStickX,
2332 settings.deadzone_lowerbound + 0.01,
2333 )),
2334 ];
2335 ctx.app
2336 .world_mut()
2337 .resource_mut::<Events<RawGamepadEvent>>()
2338 .send_batch(events);
2339 ctx.update();
2340 assert_eq!(
2341 ctx.app
2342 .world()
2343 .resource::<Events<GamepadAxisChangedEvent>>()
2344 .len(),
2345 0
2346 );
2347 }
2348
2349 #[test]
2350 fn gamepad_axis_deadzone_rounded() {
2351 let mut ctx = TestContext::new();
2352
2353 let entity = ctx.send_gamepad_connection_event(None);
2355 let settings = GamepadSettings::default().default_axis_settings;
2356
2357 let events = [
2359 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2360 entity,
2361 GamepadAxis::LeftStickX,
2362 1.0,
2363 )),
2364 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2366 entity,
2367 GamepadAxis::LeftStickX,
2368 settings.deadzone_upperbound - 0.01,
2369 )),
2370 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2371 entity,
2372 GamepadAxis::LeftStickX,
2373 1.0,
2374 )),
2375 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2377 entity,
2378 GamepadAxis::LeftStickX,
2379 settings.deadzone_lowerbound + 0.01,
2380 )),
2381 ];
2382 let results = [1.0, 0.0, 1.0, 0.0];
2383 ctx.app
2384 .world_mut()
2385 .resource_mut::<Events<RawGamepadEvent>>()
2386 .send_batch(events);
2387 ctx.update();
2388
2389 let events = ctx
2390 .app
2391 .world()
2392 .resource::<Events<GamepadAxisChangedEvent>>();
2393 let mut event_reader = events.get_cursor();
2394 for (event, result) in event_reader.read(events).zip(results) {
2395 assert_eq!(event.value, result);
2396 }
2397 assert_eq!(
2398 ctx.app
2399 .world()
2400 .resource::<Events<GamepadAxisChangedEvent>>()
2401 .len(),
2402 4
2403 );
2404 }
2405
2406 #[test]
2407 fn gamepad_axis_livezone_filter() {
2408 let mut ctx = TestContext::new();
2409
2410 let entity = ctx.send_gamepad_connection_event(None);
2412 let settings = GamepadSettings::default().default_axis_settings;
2413
2414 let events = [
2416 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2417 entity,
2418 GamepadAxis::LeftStickX,
2419 1.0,
2420 )),
2421 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2423 entity,
2424 GamepadAxis::LeftStickX,
2425 settings.livezone_upperbound + 0.01,
2426 )),
2427 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2428 entity,
2429 GamepadAxis::LeftStickX,
2430 -1.0,
2431 )),
2432 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2434 entity,
2435 GamepadAxis::LeftStickX,
2436 settings.livezone_lowerbound - 0.01,
2437 )),
2438 ];
2439 ctx.app
2440 .world_mut()
2441 .resource_mut::<Events<RawGamepadEvent>>()
2442 .send_batch(events);
2443 ctx.update();
2444 assert_eq!(
2445 ctx.app
2446 .world()
2447 .resource::<Events<GamepadAxisChangedEvent>>()
2448 .len(),
2449 2
2450 );
2451 }
2452
2453 #[test]
2454 fn gamepad_axis_livezone_rounded() {
2455 let mut ctx = TestContext::new();
2456
2457 let entity = ctx.send_gamepad_connection_event(None);
2459 let settings = GamepadSettings::default().default_axis_settings;
2460
2461 let events = [
2463 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2465 entity,
2466 GamepadAxis::LeftStickX,
2467 settings.livezone_upperbound + 0.01,
2468 )),
2469 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2471 entity,
2472 GamepadAxis::LeftStickX,
2473 settings.livezone_lowerbound - 0.01,
2474 )),
2475 ];
2476 let results = [1.0, -1.0];
2477 ctx.app
2478 .world_mut()
2479 .resource_mut::<Events<RawGamepadEvent>>()
2480 .send_batch(events);
2481 ctx.update();
2482
2483 let events = ctx
2484 .app
2485 .world()
2486 .resource::<Events<GamepadAxisChangedEvent>>();
2487 let mut event_reader = events.get_cursor();
2488 for (event, result) in event_reader.read(events).zip(results) {
2489 assert_eq!(event.value, result);
2490 }
2491 assert_eq!(
2492 ctx.app
2493 .world()
2494 .resource::<Events<GamepadAxisChangedEvent>>()
2495 .len(),
2496 2
2497 );
2498 }
2499
2500 #[test]
2501 fn gamepad_buttons_pressed() {
2502 let mut ctx = TestContext::new();
2503
2504 let entity = ctx.send_gamepad_connection_event(None);
2506 let digital_settings = GamepadSettings::default().default_button_settings;
2507
2508 let events = [RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2509 entity,
2510 GamepadButton::DPadDown,
2511 digital_settings.press_threshold,
2512 ))];
2513 ctx.app
2514 .world_mut()
2515 .resource_mut::<Events<RawGamepadEvent>>()
2516 .send_batch(events);
2517 ctx.update();
2518
2519 assert_eq!(
2520 ctx.app
2521 .world()
2522 .resource::<Events<GamepadButtonStateChangedEvent>>()
2523 .len(),
2524 1
2525 );
2526 let events = ctx
2527 .app
2528 .world()
2529 .resource::<Events<GamepadButtonStateChangedEvent>>();
2530 let mut event_reader = events.get_cursor();
2531 for event in event_reader.read(events) {
2532 assert_eq!(event.button, GamepadButton::DPadDown);
2533 assert_eq!(event.state, ButtonState::Pressed);
2534 }
2535 assert!(ctx
2536 .app
2537 .world_mut()
2538 .query::<&Gamepad>()
2539 .get(ctx.app.world(), entity)
2540 .unwrap()
2541 .pressed(GamepadButton::DPadDown));
2542
2543 ctx.app
2544 .world_mut()
2545 .resource_mut::<Events<GamepadButtonStateChangedEvent>>()
2546 .clear();
2547 ctx.update();
2548
2549 assert_eq!(
2550 ctx.app
2551 .world()
2552 .resource::<Events<GamepadButtonStateChangedEvent>>()
2553 .len(),
2554 0
2555 );
2556 assert!(ctx
2557 .app
2558 .world_mut()
2559 .query::<&Gamepad>()
2560 .get(ctx.app.world(), entity)
2561 .unwrap()
2562 .pressed(GamepadButton::DPadDown));
2563 }
2564
2565 #[test]
2566 fn gamepad_buttons_just_pressed() {
2567 let mut ctx = TestContext::new();
2568
2569 let entity = ctx.send_gamepad_connection_event(None);
2571 let digital_settings = GamepadSettings::default().default_button_settings;
2572
2573 ctx.send_raw_gamepad_event(RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2574 entity,
2575 GamepadButton::DPadDown,
2576 digital_settings.press_threshold,
2577 )));
2578 ctx.update();
2579
2580 assert!(ctx
2582 .app
2583 .world_mut()
2584 .query::<&Gamepad>()
2585 .get(ctx.app.world(), entity)
2586 .unwrap()
2587 .just_pressed(GamepadButton::DPadDown));
2588 ctx.update();
2589
2590 assert!(!ctx
2592 .app
2593 .world_mut()
2594 .query::<&Gamepad>()
2595 .get(ctx.app.world(), entity)
2596 .unwrap()
2597 .just_pressed(GamepadButton::DPadDown));
2598 }
2599 #[test]
2600 fn gamepad_buttons_released() {
2601 let mut ctx = TestContext::new();
2602
2603 let entity = ctx.send_gamepad_connection_event(None);
2605 let digital_settings = GamepadSettings::default().default_button_settings;
2606
2607 ctx.send_raw_gamepad_event(RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2608 entity,
2609 GamepadButton::DPadDown,
2610 digital_settings.press_threshold,
2611 )));
2612 ctx.update();
2613
2614 ctx.app
2615 .world_mut()
2616 .resource_mut::<Events<GamepadButtonStateChangedEvent>>()
2617 .clear();
2618 ctx.send_raw_gamepad_event(RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2619 entity,
2620 GamepadButton::DPadDown,
2621 digital_settings.release_threshold - 0.01,
2622 )));
2623 ctx.update();
2624 assert_eq!(
2625 ctx.app
2626 .world()
2627 .resource::<Events<GamepadButtonStateChangedEvent>>()
2628 .len(),
2629 1
2630 );
2631 let events = ctx
2632 .app
2633 .world()
2634 .resource::<Events<GamepadButtonStateChangedEvent>>();
2635 let mut event_reader = events.get_cursor();
2636 for event in event_reader.read(events) {
2637 assert_eq!(event.button, GamepadButton::DPadDown);
2638 assert_eq!(event.state, ButtonState::Released);
2639 }
2640 assert!(!ctx
2641 .app
2642 .world_mut()
2643 .query::<&Gamepad>()
2644 .get(ctx.app.world(), entity)
2645 .unwrap()
2646 .pressed(GamepadButton::DPadDown));
2647 ctx.app
2648 .world_mut()
2649 .resource_mut::<Events<GamepadButtonStateChangedEvent>>()
2650 .clear();
2651 ctx.update();
2652
2653 assert_eq!(
2654 ctx.app
2655 .world()
2656 .resource::<Events<GamepadButtonStateChangedEvent>>()
2657 .len(),
2658 0
2659 );
2660 }
2661
2662 #[test]
2663 fn gamepad_buttons_just_released() {
2664 let mut ctx = TestContext::new();
2665
2666 let entity = ctx.send_gamepad_connection_event(None);
2668 let digital_settings = GamepadSettings::default().default_button_settings;
2669
2670 ctx.send_raw_gamepad_event_batch([
2671 RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2672 entity,
2673 GamepadButton::DPadDown,
2674 digital_settings.press_threshold,
2675 )),
2676 RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2677 entity,
2678 GamepadButton::DPadDown,
2679 digital_settings.release_threshold - 0.01,
2680 )),
2681 ]);
2682 ctx.update();
2683
2684 assert!(ctx
2686 .app
2687 .world_mut()
2688 .query::<&Gamepad>()
2689 .get(ctx.app.world(), entity)
2690 .unwrap()
2691 .just_released(GamepadButton::DPadDown));
2692 ctx.update();
2693
2694 assert!(!ctx
2696 .app
2697 .world_mut()
2698 .query::<&Gamepad>()
2699 .get(ctx.app.world(), entity)
2700 .unwrap()
2701 .just_released(GamepadButton::DPadDown));
2702 }
2703
2704 #[test]
2705 fn gamepad_buttons_axis() {
2706 let mut ctx = TestContext::new();
2707
2708 let entity = ctx.send_gamepad_connection_event(None);
2710 let digital_settings = GamepadSettings::default().default_button_settings;
2711 let analog_settings = GamepadSettings::default().default_button_axis_settings;
2712
2713 let events = [
2715 RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2717 entity,
2718 GamepadButton::DPadDown,
2719 digital_settings.press_threshold,
2720 )),
2721 RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2723 entity,
2724 GamepadButton::DPadDown,
2725 digital_settings.release_threshold,
2726 )),
2727 RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2729 entity,
2730 GamepadButton::DPadDown,
2731 digital_settings.release_threshold - analog_settings.threshold * 1.01,
2732 )),
2733 RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2735 entity,
2736 GamepadButton::DPadDown,
2737 digital_settings.release_threshold - (analog_settings.threshold * 1.5),
2738 )),
2739 RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2741 entity,
2742 GamepadButton::DPadDown,
2743 digital_settings.release_threshold - (analog_settings.threshold * 2.02),
2744 )),
2745 ];
2746 ctx.send_raw_gamepad_event_batch(events);
2747 ctx.update();
2748 assert_eq!(
2749 ctx.app
2750 .world()
2751 .resource::<Events<GamepadButtonStateChangedEvent>>()
2752 .len(),
2753 2
2754 );
2755 assert_eq!(
2756 ctx.app
2757 .world()
2758 .resource::<Events<GamepadButtonChangedEvent>>()
2759 .len(),
2760 4
2761 );
2762 }
2763}