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 message::{Message, MessageReader, MessageWriter},
14 name::Name,
15 reflect::ReflectMessage,
16 system::{Commands, Query},
17};
18use bevy_math::ops;
19use bevy_math::Vec2;
20use bevy_platform::collections::HashMap;
21#[cfg(feature = "bevy_reflect")]
22use bevy_reflect::{std_traits::ReflectDefault, Reflect};
23#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
24use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
25use derive_more::derive::From;
26use log::{info, warn};
27use thiserror::Error;
28
29#[derive(Message, Debug, Clone, PartialEq, From)]
37#[cfg_attr(
38 feature = "bevy_reflect",
39 derive(Reflect),
40 reflect(Debug, PartialEq, Clone, Message)
41)]
42#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
43#[cfg_attr(
44 all(feature = "serialize", feature = "bevy_reflect"),
45 reflect(Serialize, Deserialize)
46)]
47pub enum GamepadEvent {
48 Connection(GamepadConnectionEvent),
50 Button(GamepadButtonChangedEvent),
52 Axis(GamepadAxisChangedEvent),
54}
55
56#[derive(Message, Debug, Clone, PartialEq, From)]
64#[cfg_attr(
65 feature = "bevy_reflect",
66 derive(Reflect),
67 reflect(Debug, PartialEq, Clone, Message)
68)]
69#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
70#[cfg_attr(
71 all(feature = "serialize", feature = "bevy_reflect"),
72 reflect(Serialize, Deserialize)
73)]
74pub enum RawGamepadEvent {
75 Connection(GamepadConnectionEvent),
77 Button(RawGamepadButtonChangedEvent),
79 Axis(RawGamepadAxisChangedEvent),
81}
82
83#[derive(Message, Debug, Copy, Clone, PartialEq)]
85#[cfg_attr(
86 feature = "bevy_reflect",
87 derive(Reflect),
88 reflect(Debug, PartialEq, Clone, Message)
89)]
90#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
91#[cfg_attr(
92 all(feature = "serialize", feature = "bevy_reflect"),
93 reflect(Serialize, Deserialize)
94)]
95pub struct RawGamepadButtonChangedEvent {
96 pub gamepad: Entity,
98 pub button: GamepadButton,
100 pub value: f32,
102}
103
104impl RawGamepadButtonChangedEvent {
105 pub fn new(gamepad: Entity, button_type: GamepadButton, value: f32) -> Self {
107 Self {
108 gamepad,
109 button: button_type,
110 value,
111 }
112 }
113}
114
115#[derive(Message, Debug, Copy, Clone, PartialEq)]
117#[cfg_attr(
118 feature = "bevy_reflect",
119 derive(Reflect),
120 reflect(Debug, PartialEq, Clone, Message)
121)]
122#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
123#[cfg_attr(
124 all(feature = "serialize", feature = "bevy_reflect"),
125 reflect(Serialize, Deserialize)
126)]
127pub struct RawGamepadAxisChangedEvent {
128 pub gamepad: Entity,
130 pub axis: GamepadAxis,
132 pub value: f32,
134}
135
136impl RawGamepadAxisChangedEvent {
137 pub fn new(gamepad: Entity, axis_type: GamepadAxis, value: f32) -> Self {
139 Self {
140 gamepad,
141 axis: axis_type,
142 value,
143 }
144 }
145}
146
147#[derive(Message, Debug, Clone, PartialEq)]
150#[cfg_attr(
151 feature = "bevy_reflect",
152 derive(Reflect),
153 reflect(Debug, PartialEq, Clone, Message)
154)]
155#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
156#[cfg_attr(
157 all(feature = "serialize", feature = "bevy_reflect"),
158 reflect(Serialize, Deserialize)
159)]
160pub struct GamepadConnectionEvent {
161 pub gamepad: Entity,
163 pub connection: GamepadConnection,
165}
166
167impl GamepadConnectionEvent {
168 pub fn new(gamepad: Entity, connection: GamepadConnection) -> Self {
170 Self {
171 gamepad,
172 connection,
173 }
174 }
175
176 pub fn connected(&self) -> bool {
178 matches!(self.connection, GamepadConnection::Connected { .. })
179 }
180
181 pub fn disconnected(&self) -> bool {
183 !self.connected()
184 }
185}
186
187#[derive(Message, Debug, Clone, Copy, PartialEq, Eq)]
189#[cfg_attr(
190 feature = "bevy_reflect",
191 derive(Reflect),
192 reflect(Debug, PartialEq, Clone, Message)
193)]
194#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
195#[cfg_attr(
196 all(feature = "serialize", feature = "bevy_reflect"),
197 reflect(Serialize, Deserialize)
198)]
199pub struct GamepadButtonStateChangedEvent {
200 pub entity: Entity,
202 pub button: GamepadButton,
204 pub state: ButtonState,
206}
207
208impl GamepadButtonStateChangedEvent {
209 pub fn new(entity: Entity, button: GamepadButton, state: ButtonState) -> Self {
211 Self {
212 entity,
213 button,
214 state,
215 }
216 }
217}
218
219#[derive(Message, Debug, Clone, Copy, PartialEq)]
221#[cfg_attr(
222 feature = "bevy_reflect",
223 derive(Reflect),
224 reflect(Debug, PartialEq, Clone, Message)
225)]
226#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
227#[cfg_attr(
228 all(feature = "serialize", feature = "bevy_reflect"),
229 reflect(Serialize, Deserialize)
230)]
231pub struct GamepadButtonChangedEvent {
232 pub entity: Entity,
234 pub button: GamepadButton,
236 pub state: ButtonState,
238 pub value: f32,
240}
241
242impl GamepadButtonChangedEvent {
243 pub fn new(entity: Entity, button: GamepadButton, state: ButtonState, value: f32) -> Self {
245 Self {
246 entity,
247 button,
248 state,
249 value,
250 }
251 }
252}
253
254#[derive(Message, Debug, Clone, Copy, PartialEq)]
256#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
257#[cfg_attr(
258 feature = "bevy_reflect",
259 derive(Reflect),
260 reflect(Debug, PartialEq, Clone, Message)
261)]
262#[cfg_attr(
263 all(feature = "bevy_reflect", feature = "serialize"),
264 reflect(Serialize, Deserialize)
265)]
266pub struct GamepadAxisChangedEvent {
267 pub entity: Entity,
269 pub axis: GamepadAxis,
271 pub value: f32,
273}
274
275impl GamepadAxisChangedEvent {
276 pub fn new(entity: Entity, axis: GamepadAxis, value: f32) -> Self {
278 Self {
279 entity,
280 axis,
281 value,
282 }
283 }
284}
285
286#[derive(Error, Debug, PartialEq)]
288pub enum AxisSettingsError {
289 #[error("invalid livezone_lowerbound {0}, expected value [-1.0..=0.0]")]
291 LiveZoneLowerBoundOutOfRange(f32),
292 #[error("invalid deadzone_lowerbound {0}, expected value [-1.0..=0.0]")]
294 DeadZoneLowerBoundOutOfRange(f32),
295 #[error("invalid deadzone_upperbound {0}, expected value [0.0..=1.0]")]
297 DeadZoneUpperBoundOutOfRange(f32),
298 #[error("invalid livezone_upperbound {0}, expected value [0.0..=1.0]")]
300 LiveZoneUpperBoundOutOfRange(f32),
301 #[error("invalid parameter values livezone_lowerbound {} deadzone_lowerbound {}, expected livezone_lowerbound <= deadzone_lowerbound", livezone_lowerbound, deadzone_lowerbound)]
303 LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
304 livezone_lowerbound: f32,
306 deadzone_lowerbound: f32,
308 },
309 #[error("invalid parameter values livezone_upperbound {} deadzone_upperbound {}, expected deadzone_upperbound <= livezone_upperbound", livezone_upperbound, deadzone_upperbound)]
311 DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
312 livezone_upperbound: f32,
314 deadzone_upperbound: f32,
316 },
317 #[error("invalid threshold {0}, expected 0.0 <= threshold <= 2.0")]
319 Threshold(f32),
320}
321
322#[derive(Error, Debug, PartialEq)]
324pub enum ButtonSettingsError {
325 #[error("invalid release_threshold {0}, expected value [0.0..=1.0]")]
327 ReleaseThresholdOutOfRange(f32),
328 #[error("invalid press_threshold {0}, expected [0.0..=1.0]")]
330 PressThresholdOutOfRange(f32),
331 #[error("invalid parameter values release_threshold {} press_threshold {}, expected release_threshold <= press_threshold", release_threshold, press_threshold)]
333 ReleaseThresholdGreaterThanPressThreshold {
334 press_threshold: f32,
336 release_threshold: f32,
338 },
339}
340
341#[derive(Component, Debug)]
370#[cfg_attr(
371 feature = "bevy_reflect",
372 derive(Reflect),
373 reflect(Debug, Component, Default)
374)]
375#[require(GamepadSettings)]
376pub struct Gamepad {
377 pub(crate) vendor_id: Option<u16>,
379
380 pub(crate) product_id: Option<u16>,
382
383 pub(crate) digital: ButtonInput<GamepadButton>,
385
386 pub(crate) analog: Axis<GamepadInput>,
388}
389
390impl Gamepad {
391 pub fn vendor_id(&self) -> Option<u16> {
393 self.vendor_id
394 }
395
396 pub fn product_id(&self) -> Option<u16> {
400 self.product_id
401 }
402
403 pub fn get(&self, input: impl Into<GamepadInput>) -> Option<f32> {
407 self.analog.get(input.into())
408 }
409
410 pub fn get_unclamped(&self, input: impl Into<GamepadInput>) -> Option<f32> {
414 self.analog.get_unclamped(input.into())
415 }
416
417 pub fn left_stick(&self) -> Vec2 {
419 Vec2 {
420 x: self.get(GamepadAxis::LeftStickX).unwrap_or(0.0),
421 y: self.get(GamepadAxis::LeftStickY).unwrap_or(0.0),
422 }
423 }
424
425 pub fn right_stick(&self) -> Vec2 {
427 Vec2 {
428 x: self.get(GamepadAxis::RightStickX).unwrap_or(0.0),
429 y: self.get(GamepadAxis::RightStickY).unwrap_or(0.0),
430 }
431 }
432
433 pub fn dpad(&self) -> Vec2 {
435 Vec2 {
436 x: self.get(GamepadButton::DPadRight).unwrap_or(0.0)
437 - self.get(GamepadButton::DPadLeft).unwrap_or(0.0),
438 y: self.get(GamepadButton::DPadUp).unwrap_or(0.0)
439 - self.get(GamepadButton::DPadDown).unwrap_or(0.0),
440 }
441 }
442
443 pub fn pressed(&self, button_type: GamepadButton) -> bool {
445 self.digital.pressed(button_type)
446 }
447
448 pub fn any_pressed(&self, button_inputs: impl IntoIterator<Item = GamepadButton>) -> bool {
450 self.digital.any_pressed(button_inputs)
451 }
452
453 pub fn all_pressed(&self, button_inputs: impl IntoIterator<Item = GamepadButton>) -> bool {
455 self.digital.all_pressed(button_inputs)
456 }
457
458 pub fn just_pressed(&self, button_type: GamepadButton) -> bool {
462 self.digital.just_pressed(button_type)
463 }
464
465 pub fn any_just_pressed(&self, button_inputs: impl IntoIterator<Item = GamepadButton>) -> bool {
467 self.digital.any_just_pressed(button_inputs)
468 }
469
470 pub fn all_just_pressed(&self, button_inputs: impl IntoIterator<Item = GamepadButton>) -> bool {
472 self.digital.all_just_pressed(button_inputs)
473 }
474
475 pub fn just_released(&self, button_type: GamepadButton) -> bool {
479 self.digital.just_released(button_type)
480 }
481
482 pub fn any_just_released(
484 &self,
485 button_inputs: impl IntoIterator<Item = GamepadButton>,
486 ) -> bool {
487 self.digital.any_just_released(button_inputs)
488 }
489
490 pub fn all_just_released(
492 &self,
493 button_inputs: impl IntoIterator<Item = GamepadButton>,
494 ) -> bool {
495 self.digital.all_just_released(button_inputs)
496 }
497
498 pub fn get_pressed(&self) -> impl Iterator<Item = &GamepadButton> {
502 self.digital.get_pressed()
503 }
504
505 pub fn get_just_pressed(&self) -> impl Iterator<Item = &GamepadButton> {
509 self.digital.get_just_pressed()
510 }
511
512 pub fn get_just_released(&self) -> impl Iterator<Item = &GamepadButton> {
516 self.digital.get_just_released()
517 }
518
519 pub fn get_analog_axes(&self) -> impl Iterator<Item = &GamepadInput> {
521 self.analog.all_axes()
522 }
523
524 pub fn digital(&self) -> &ButtonInput<GamepadButton> {
526 &self.digital
527 }
528
529 pub fn digital_mut(&mut self) -> &mut ButtonInput<GamepadButton> {
531 &mut self.digital
532 }
533
534 pub fn analog(&self) -> &Axis<GamepadInput> {
536 &self.analog
537 }
538
539 pub fn analog_mut(&mut self) -> &mut Axis<GamepadInput> {
541 &mut self.analog
542 }
543}
544
545impl Default for Gamepad {
546 fn default() -> Self {
547 let mut analog = Axis::default();
548 for button in GamepadButton::all().iter().copied() {
549 analog.set(button, 0.0);
550 }
551 for axis_type in GamepadAxis::all().iter().copied() {
552 analog.set(axis_type, 0.0);
553 }
554
555 Self {
556 vendor_id: None,
557 product_id: None,
558 digital: Default::default(),
559 analog,
560 }
561 }
562}
563
564#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
571#[cfg_attr(
572 feature = "bevy_reflect",
573 derive(Reflect),
574 reflect(Debug, Hash, PartialEq, Clone)
575)]
576#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
577#[cfg_attr(
578 all(feature = "serialize", feature = "bevy_reflect"),
579 reflect(Serialize, Deserialize)
580)]
581pub enum GamepadButton {
582 South,
584 East,
586 North,
588 West,
590
591 C,
593 Z,
595
596 LeftTrigger,
598 LeftTrigger2,
600 RightTrigger,
602 RightTrigger2,
604 Select,
606 Start,
608 Mode,
610
611 LeftThumb,
613 RightThumb,
615
616 DPadUp,
618 DPadDown,
620 DPadLeft,
622 DPadRight,
624
625 Other(u32),
629}
630
631impl GamepadButton {
632 pub const fn all() -> [GamepadButton; 19] {
634 [
635 GamepadButton::South,
636 GamepadButton::East,
637 GamepadButton::North,
638 GamepadButton::West,
639 GamepadButton::C,
640 GamepadButton::Z,
641 GamepadButton::LeftTrigger,
642 GamepadButton::LeftTrigger2,
643 GamepadButton::RightTrigger,
644 GamepadButton::RightTrigger2,
645 GamepadButton::Select,
646 GamepadButton::Start,
647 GamepadButton::Mode,
648 GamepadButton::LeftThumb,
649 GamepadButton::RightThumb,
650 GamepadButton::DPadUp,
651 GamepadButton::DPadDown,
652 GamepadButton::DPadLeft,
653 GamepadButton::DPadRight,
654 ]
655 }
656}
657
658#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
665#[cfg_attr(
666 feature = "bevy_reflect",
667 derive(Reflect),
668 reflect(Debug, PartialEq, Hash, Clone)
669)]
670#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
671#[cfg_attr(
672 all(feature = "serialize", feature = "bevy_reflect"),
673 reflect(Serialize, Deserialize)
674)]
675pub enum GamepadAxis {
676 LeftStickX,
678 LeftStickY,
680 LeftZ,
683 RightStickX,
685 RightStickY,
687 RightZ,
690 Other(u32),
694}
695
696impl GamepadAxis {
697 pub const fn all() -> [GamepadAxis; 6] {
699 [
700 GamepadAxis::LeftStickX,
701 GamepadAxis::LeftStickY,
702 GamepadAxis::LeftZ,
703 GamepadAxis::RightStickX,
704 GamepadAxis::RightStickY,
705 GamepadAxis::RightZ,
706 ]
707 }
708}
709
710#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, From)]
713#[cfg_attr(
714 feature = "bevy_reflect",
715 derive(Reflect),
716 reflect(Debug, Hash, PartialEq, Clone)
717)]
718pub enum GamepadInput {
719 Axis(GamepadAxis),
721 Button(GamepadButton),
723}
724
725#[derive(Component, Clone, Default, Debug)]
739#[cfg_attr(
740 feature = "bevy_reflect",
741 derive(Reflect),
742 reflect(Debug, Default, Component, Clone)
743)]
744pub struct GamepadSettings {
745 pub default_button_settings: ButtonSettings,
747 pub default_axis_settings: AxisSettings,
749 pub default_button_axis_settings: ButtonAxisSettings,
751 pub button_settings: HashMap<GamepadButton, ButtonSettings>,
753 pub axis_settings: HashMap<GamepadAxis, AxisSettings>,
755 pub button_axis_settings: HashMap<GamepadButton, ButtonAxisSettings>,
757}
758
759impl GamepadSettings {
760 pub fn get_button_settings(&self, button: GamepadButton) -> &ButtonSettings {
773 self.button_settings
774 .get(&button)
775 .unwrap_or(&self.default_button_settings)
776 }
777
778 pub fn get_axis_settings(&self, axis: GamepadAxis) -> &AxisSettings {
791 self.axis_settings
792 .get(&axis)
793 .unwrap_or(&self.default_axis_settings)
794 }
795
796 pub fn get_button_axis_settings(&self, button: GamepadButton) -> &ButtonAxisSettings {
809 self.button_axis_settings
810 .get(&button)
811 .unwrap_or(&self.default_button_axis_settings)
812 }
813}
814
815#[derive(Debug, PartialEq, Clone)]
823#[cfg_attr(
824 feature = "bevy_reflect",
825 derive(Reflect),
826 reflect(Debug, Default, Clone)
827)]
828pub struct ButtonSettings {
829 press_threshold: f32,
830 release_threshold: f32,
831}
832
833impl Default for ButtonSettings {
834 fn default() -> Self {
835 ButtonSettings {
836 press_threshold: 0.75,
837 release_threshold: 0.65,
838 }
839 }
840}
841
842impl ButtonSettings {
843 pub fn new(
860 press_threshold: f32,
861 release_threshold: f32,
862 ) -> Result<ButtonSettings, ButtonSettingsError> {
863 if !(0.0..=1.0).contains(&release_threshold) {
864 Err(ButtonSettingsError::ReleaseThresholdOutOfRange(
865 release_threshold,
866 ))
867 } else if !(0.0..=1.0).contains(&press_threshold) {
868 Err(ButtonSettingsError::PressThresholdOutOfRange(
869 press_threshold,
870 ))
871 } else if release_threshold > press_threshold {
872 Err(
873 ButtonSettingsError::ReleaseThresholdGreaterThanPressThreshold {
874 press_threshold,
875 release_threshold,
876 },
877 )
878 } else {
879 Ok(ButtonSettings {
880 press_threshold,
881 release_threshold,
882 })
883 }
884 }
885
886 pub fn is_pressed(&self, value: f32) -> bool {
890 value >= self.press_threshold
891 }
892
893 pub fn is_released(&self, value: f32) -> bool {
897 value <= self.release_threshold
898 }
899
900 pub fn press_threshold(&self) -> f32 {
902 self.press_threshold
903 }
904
905 pub fn try_set_press_threshold(&mut self, value: f32) -> Result<(), ButtonSettingsError> {
913 if (self.release_threshold..=1.0).contains(&value) {
914 self.press_threshold = value;
915 Ok(())
916 } else if !(0.0..1.0).contains(&value) {
917 Err(ButtonSettingsError::PressThresholdOutOfRange(value))
918 } else {
919 Err(
920 ButtonSettingsError::ReleaseThresholdGreaterThanPressThreshold {
921 press_threshold: value,
922 release_threshold: self.release_threshold,
923 },
924 )
925 }
926 }
927
928 pub fn set_press_threshold(&mut self, value: f32) -> f32 {
933 self.try_set_press_threshold(value).ok();
934 self.press_threshold
935 }
936
937 pub fn release_threshold(&self) -> f32 {
939 self.release_threshold
940 }
941
942 pub fn try_set_release_threshold(&mut self, value: f32) -> Result<(), ButtonSettingsError> {
950 if (0.0..=self.press_threshold).contains(&value) {
951 self.release_threshold = value;
952 Ok(())
953 } else if !(0.0..1.0).contains(&value) {
954 Err(ButtonSettingsError::ReleaseThresholdOutOfRange(value))
955 } else {
956 Err(
957 ButtonSettingsError::ReleaseThresholdGreaterThanPressThreshold {
958 press_threshold: self.press_threshold,
959 release_threshold: value,
960 },
961 )
962 }
963 }
964
965 pub fn set_release_threshold(&mut self, value: f32) -> f32 {
970 self.try_set_release_threshold(value).ok();
971 self.release_threshold
972 }
973}
974
975#[derive(Debug, Clone, PartialEq)]
987#[cfg_attr(
988 feature = "bevy_reflect",
989 derive(Reflect),
990 reflect(Debug, PartialEq, Default, Clone)
991)]
992pub struct AxisSettings {
993 livezone_upperbound: f32,
995 deadzone_upperbound: f32,
997 deadzone_lowerbound: f32,
999 livezone_lowerbound: f32,
1001 threshold: f32,
1003}
1004
1005impl Default for AxisSettings {
1006 fn default() -> Self {
1007 AxisSettings {
1008 livezone_upperbound: 1.0,
1009 deadzone_upperbound: 0.05,
1010 deadzone_lowerbound: -0.05,
1011 livezone_lowerbound: -1.0,
1012 threshold: 0.01,
1013 }
1014 }
1015}
1016
1017impl AxisSettings {
1018 pub fn new(
1040 livezone_lowerbound: f32,
1041 deadzone_lowerbound: f32,
1042 deadzone_upperbound: f32,
1043 livezone_upperbound: f32,
1044 threshold: f32,
1045 ) -> Result<AxisSettings, AxisSettingsError> {
1046 if !(-1.0..=0.0).contains(&livezone_lowerbound) {
1047 Err(AxisSettingsError::LiveZoneLowerBoundOutOfRange(
1048 livezone_lowerbound,
1049 ))
1050 } else if !(-1.0..=0.0).contains(&deadzone_lowerbound) {
1051 Err(AxisSettingsError::DeadZoneLowerBoundOutOfRange(
1052 deadzone_lowerbound,
1053 ))
1054 } else if !(0.0..=1.0).contains(&deadzone_upperbound) {
1055 Err(AxisSettingsError::DeadZoneUpperBoundOutOfRange(
1056 deadzone_upperbound,
1057 ))
1058 } else if !(0.0..=1.0).contains(&livezone_upperbound) {
1059 Err(AxisSettingsError::LiveZoneUpperBoundOutOfRange(
1060 livezone_upperbound,
1061 ))
1062 } else if livezone_lowerbound > deadzone_lowerbound {
1063 Err(
1064 AxisSettingsError::LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
1065 livezone_lowerbound,
1066 deadzone_lowerbound,
1067 },
1068 )
1069 } else if deadzone_upperbound > livezone_upperbound {
1070 Err(
1071 AxisSettingsError::DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
1072 livezone_upperbound,
1073 deadzone_upperbound,
1074 },
1075 )
1076 } else if !(0.0..=2.0).contains(&threshold) {
1077 Err(AxisSettingsError::Threshold(threshold))
1078 } else {
1079 Ok(Self {
1080 livezone_lowerbound,
1081 deadzone_lowerbound,
1082 deadzone_upperbound,
1083 livezone_upperbound,
1084 threshold,
1085 })
1086 }
1087 }
1088
1089 pub fn livezone_upperbound(&self) -> f32 {
1091 self.livezone_upperbound
1092 }
1093
1094 pub fn try_set_livezone_upperbound(&mut self, value: f32) -> Result<(), AxisSettingsError> {
1102 if !(0.0..=1.0).contains(&value) {
1103 Err(AxisSettingsError::LiveZoneUpperBoundOutOfRange(value))
1104 } else if value < self.deadzone_upperbound {
1105 Err(
1106 AxisSettingsError::DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
1107 livezone_upperbound: value,
1108 deadzone_upperbound: self.deadzone_upperbound,
1109 },
1110 )
1111 } else {
1112 self.livezone_upperbound = value;
1113 Ok(())
1114 }
1115 }
1116
1117 pub fn set_livezone_upperbound(&mut self, value: f32) -> f32 {
1123 self.try_set_livezone_upperbound(value).ok();
1124 self.livezone_upperbound
1125 }
1126
1127 pub fn deadzone_upperbound(&self) -> f32 {
1129 self.deadzone_upperbound
1130 }
1131
1132 pub fn try_set_deadzone_upperbound(&mut self, value: f32) -> Result<(), AxisSettingsError> {
1140 if !(0.0..=1.0).contains(&value) {
1141 Err(AxisSettingsError::DeadZoneUpperBoundOutOfRange(value))
1142 } else if self.livezone_upperbound < value {
1143 Err(
1144 AxisSettingsError::DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
1145 livezone_upperbound: self.livezone_upperbound,
1146 deadzone_upperbound: value,
1147 },
1148 )
1149 } else {
1150 self.deadzone_upperbound = value;
1151 Ok(())
1152 }
1153 }
1154
1155 pub fn set_deadzone_upperbound(&mut self, value: f32) -> f32 {
1161 self.try_set_deadzone_upperbound(value).ok();
1162 self.deadzone_upperbound
1163 }
1164
1165 pub fn livezone_lowerbound(&self) -> f32 {
1167 self.livezone_lowerbound
1168 }
1169
1170 pub fn try_set_livezone_lowerbound(&mut self, value: f32) -> Result<(), AxisSettingsError> {
1178 if !(-1.0..=0.0).contains(&value) {
1179 Err(AxisSettingsError::LiveZoneLowerBoundOutOfRange(value))
1180 } else if value > self.deadzone_lowerbound {
1181 Err(
1182 AxisSettingsError::LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
1183 livezone_lowerbound: value,
1184 deadzone_lowerbound: self.deadzone_lowerbound,
1185 },
1186 )
1187 } else {
1188 self.livezone_lowerbound = value;
1189 Ok(())
1190 }
1191 }
1192
1193 pub fn set_livezone_lowerbound(&mut self, value: f32) -> f32 {
1199 self.try_set_livezone_lowerbound(value).ok();
1200 self.livezone_lowerbound
1201 }
1202
1203 pub fn deadzone_lowerbound(&self) -> f32 {
1205 self.deadzone_lowerbound
1206 }
1207
1208 pub fn try_set_deadzone_lowerbound(&mut self, value: f32) -> Result<(), AxisSettingsError> {
1216 if !(-1.0..=0.0).contains(&value) {
1217 Err(AxisSettingsError::DeadZoneLowerBoundOutOfRange(value))
1218 } else if self.livezone_lowerbound > value {
1219 Err(
1220 AxisSettingsError::LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
1221 livezone_lowerbound: self.livezone_lowerbound,
1222 deadzone_lowerbound: value,
1223 },
1224 )
1225 } else {
1226 self.deadzone_lowerbound = value;
1227 Ok(())
1228 }
1229 }
1230
1231 pub fn set_deadzone_lowerbound(&mut self, value: f32) -> f32 {
1237 self.try_set_deadzone_lowerbound(value).ok();
1238 self.deadzone_lowerbound
1239 }
1240
1241 pub fn threshold(&self) -> f32 {
1243 self.threshold
1244 }
1245
1246 pub fn try_set_threshold(&mut self, value: f32) -> Result<(), AxisSettingsError> {
1252 if !(0.0..=2.0).contains(&value) {
1253 Err(AxisSettingsError::Threshold(value))
1254 } else {
1255 self.threshold = value;
1256 Ok(())
1257 }
1258 }
1259
1260 pub fn set_threshold(&mut self, value: f32) -> f32 {
1265 self.try_set_threshold(value).ok();
1266 self.threshold
1267 }
1268
1269 pub fn clamp(&self, raw_value: f32) -> f32 {
1271 if self.deadzone_lowerbound <= raw_value && raw_value <= self.deadzone_upperbound {
1272 0.0
1273 } else if raw_value >= self.livezone_upperbound {
1274 1.0
1275 } else if raw_value <= self.livezone_lowerbound {
1276 -1.0
1277 } else {
1278 raw_value
1279 }
1280 }
1281
1282 fn should_register_change(&self, new_raw_value: f32, old_raw_value: Option<f32>) -> bool {
1285 match old_raw_value {
1286 None => true,
1287 Some(old_raw_value) => ops::abs(new_raw_value - old_raw_value) >= self.threshold,
1288 }
1289 }
1290
1291 fn filter(
1296 &self,
1297 new_raw_value: f32,
1298 old_raw_value: Option<f32>,
1299 ) -> Option<FilteredAxisPosition> {
1300 let clamped_unscaled = self.clamp(new_raw_value);
1301 match self.should_register_change(clamped_unscaled, old_raw_value) {
1302 true => Some(FilteredAxisPosition {
1303 scaled: self.get_axis_position_from_value(clamped_unscaled),
1304 raw: new_raw_value,
1305 }),
1306 false => None,
1307 }
1308 }
1309
1310 #[inline(always)]
1311 fn get_axis_position_from_value(&self, value: f32) -> ScaledAxisWithDeadZonePosition {
1312 if value < self.deadzone_upperbound && value > self.deadzone_lowerbound {
1313 ScaledAxisWithDeadZonePosition::Dead
1314 } else if value > self.livezone_upperbound {
1315 ScaledAxisWithDeadZonePosition::AboveHigh
1316 } else if value < self.livezone_lowerbound {
1317 ScaledAxisWithDeadZonePosition::BelowLow
1318 } else if value >= self.deadzone_upperbound {
1319 ScaledAxisWithDeadZonePosition::High(linear_remapping(
1320 value,
1321 self.deadzone_upperbound..=self.livezone_upperbound,
1322 0.0..=1.0,
1323 ))
1324 } else if value <= self.deadzone_lowerbound {
1325 ScaledAxisWithDeadZonePosition::Low(linear_remapping(
1326 value,
1327 self.livezone_lowerbound..=self.deadzone_lowerbound,
1328 -1.0..=0.0,
1329 ))
1330 } else {
1331 unreachable!();
1332 }
1333 }
1334}
1335
1336fn linear_remapping(value: f32, old: RangeInclusive<f32>, new: RangeInclusive<f32>) -> f32 {
1338 ((value - old.start()) / (old.end() - old.start())) * (new.end() - new.start()) + new.start()
1340}
1341
1342#[derive(Debug, Clone, Copy)]
1343enum ScaledAxisWithDeadZonePosition {
1345 BelowLow,
1347 Low(f32),
1349 Dead,
1351 High(f32),
1353 AboveHigh,
1355}
1356
1357struct FilteredAxisPosition {
1358 scaled: ScaledAxisWithDeadZonePosition,
1359 raw: f32,
1360}
1361
1362impl ScaledAxisWithDeadZonePosition {
1363 fn to_f32(self) -> f32 {
1365 match self {
1366 ScaledAxisWithDeadZonePosition::BelowLow => -1.,
1367 ScaledAxisWithDeadZonePosition::Low(scaled)
1368 | ScaledAxisWithDeadZonePosition::High(scaled) => scaled,
1369 ScaledAxisWithDeadZonePosition::Dead => 0.,
1370 ScaledAxisWithDeadZonePosition::AboveHigh => 1.,
1371 }
1372 }
1373}
1374
1375#[derive(Debug, Clone, Copy)]
1376enum ScaledAxisPosition {
1378 ClampedLow,
1380 Scaled(f32),
1382 ClampedHigh,
1384}
1385
1386struct FilteredButtonAxisPosition {
1387 scaled: ScaledAxisPosition,
1388 raw: f32,
1389}
1390
1391impl ScaledAxisPosition {
1392 fn to_f32(self) -> f32 {
1394 match self {
1395 ScaledAxisPosition::ClampedLow => 0.,
1396 ScaledAxisPosition::Scaled(scaled) => scaled,
1397 ScaledAxisPosition::ClampedHigh => 1.,
1398 }
1399 }
1400}
1401
1402#[derive(Debug, Clone)]
1415#[cfg_attr(
1416 feature = "bevy_reflect",
1417 derive(Reflect),
1418 reflect(Debug, Default, Clone)
1419)]
1420pub struct ButtonAxisSettings {
1421 pub high: f32,
1423 pub low: f32,
1425 pub threshold: f32,
1427}
1428
1429impl Default for ButtonAxisSettings {
1430 fn default() -> Self {
1431 ButtonAxisSettings {
1432 high: 0.95,
1433 low: 0.05,
1434 threshold: 0.01,
1435 }
1436 }
1437}
1438
1439impl ButtonAxisSettings {
1440 fn clamp(&self, raw_value: f32) -> f32 {
1447 if raw_value <= self.low {
1448 return 0.0;
1449 }
1450 if raw_value >= self.high {
1451 return 1.0;
1452 }
1453
1454 raw_value
1455 }
1456
1457 fn should_register_change(&self, new_raw_value: f32, old_raw_value: Option<f32>) -> bool {
1460 match old_raw_value {
1461 None => true,
1462 Some(old_raw_value) => ops::abs(new_raw_value - old_raw_value) >= self.threshold,
1463 }
1464 }
1465
1466 fn filter(
1471 &self,
1472 new_raw_value: f32,
1473 old_raw_value: Option<f32>,
1474 ) -> Option<FilteredButtonAxisPosition> {
1475 let clamped_unscaled = self.clamp(new_raw_value);
1476 match self.should_register_change(clamped_unscaled, old_raw_value) {
1477 true => Some(FilteredButtonAxisPosition {
1478 scaled: self.get_axis_position_from_value(clamped_unscaled),
1479 raw: new_raw_value,
1480 }),
1481 false => None,
1482 }
1483 }
1484
1485 fn get_axis_position_from_value(&self, value: f32) -> ScaledAxisPosition {
1492 if value <= self.low {
1493 ScaledAxisPosition::ClampedLow
1494 } else if value >= self.high {
1495 ScaledAxisPosition::ClampedHigh
1496 } else {
1497 ScaledAxisPosition::Scaled(linear_remapping(value, self.low..=self.high, 0.0..=1.0))
1498 }
1499 }
1500}
1501
1502pub fn gamepad_connection_system(
1512 mut commands: Commands,
1513 mut connection_events: MessageReader<GamepadConnectionEvent>,
1514) {
1515 for connection_event in connection_events.read() {
1516 let id = connection_event.gamepad;
1517 match &connection_event.connection {
1518 GamepadConnection::Connected {
1519 name,
1520 vendor_id,
1521 product_id,
1522 } => {
1523 let Ok(mut gamepad) = commands.get_entity(id) else {
1524 warn!("Gamepad {id} removed before handling connection event.");
1525 continue;
1526 };
1527 gamepad.insert((
1528 Name::new(name.clone()),
1529 Gamepad {
1530 vendor_id: *vendor_id,
1531 product_id: *product_id,
1532 ..Default::default()
1533 },
1534 ));
1535 info!("Gamepad {id} connected.");
1536 }
1537 GamepadConnection::Disconnected => {
1538 let Ok(mut gamepad) = commands.get_entity(id) else {
1539 warn!("Gamepad {id} removed before handling disconnection event. You can ignore this if you manually removed it.");
1540 continue;
1541 };
1542 gamepad.remove::<Gamepad>();
1546 info!("Gamepad {id} disconnected.");
1547 }
1548 }
1549 }
1550}
1551
1552#[derive(Debug, Clone, PartialEq)]
1557#[cfg_attr(
1558 feature = "bevy_reflect",
1559 derive(Reflect),
1560 reflect(Debug, PartialEq, Clone)
1561)]
1562#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1563#[cfg_attr(
1564 all(feature = "serialize", feature = "bevy_reflect"),
1565 reflect(Serialize, Deserialize)
1566)]
1567pub enum GamepadConnection {
1568 Connected {
1570 name: String,
1576
1577 vendor_id: Option<u16>,
1579
1580 product_id: Option<u16>,
1582 },
1583 Disconnected,
1585}
1586
1587pub fn gamepad_event_processing_system(
1590 mut gamepads: Query<(&mut Gamepad, &GamepadSettings)>,
1591 mut raw_events: MessageReader<RawGamepadEvent>,
1592 mut processed_events: MessageWriter<GamepadEvent>,
1593 mut processed_axis_events: MessageWriter<GamepadAxisChangedEvent>,
1594 mut processed_digital_events: MessageWriter<GamepadButtonStateChangedEvent>,
1595 mut processed_analog_events: MessageWriter<GamepadButtonChangedEvent>,
1596) {
1597 for (mut gamepad, _) in gamepads.iter_mut() {
1599 gamepad.bypass_change_detection().digital.clear();
1600 }
1601
1602 for event in raw_events.read() {
1603 match event {
1604 RawGamepadEvent::Connection(send_event) => {
1606 processed_events.write(GamepadEvent::from(send_event.clone()));
1607 }
1608 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent {
1609 gamepad,
1610 axis,
1611 value,
1612 }) => {
1613 let (gamepad, axis, value) = (*gamepad, *axis, *value);
1614 let Ok((mut gamepad_axis, gamepad_settings)) = gamepads.get_mut(gamepad) else {
1615 continue;
1616 };
1617 let Some(filtered_value) = gamepad_settings
1618 .get_axis_settings(axis)
1619 .filter(value, gamepad_axis.get(axis))
1620 else {
1621 continue;
1622 };
1623 gamepad_axis.analog.set(axis, filtered_value.raw);
1624 let send_event =
1625 GamepadAxisChangedEvent::new(gamepad, axis, filtered_value.scaled.to_f32());
1626 processed_axis_events.write(send_event);
1627 processed_events.write(GamepadEvent::from(send_event));
1628 }
1629 RawGamepadEvent::Button(RawGamepadButtonChangedEvent {
1630 gamepad,
1631 button,
1632 value,
1633 }) => {
1634 let (gamepad, button, value) = (*gamepad, *button, *value);
1635 let Ok((mut gamepad_buttons, settings)) = gamepads.get_mut(gamepad) else {
1636 continue;
1637 };
1638 let Some(filtered_value) = settings
1639 .get_button_axis_settings(button)
1640 .filter(value, gamepad_buttons.get(button))
1641 else {
1642 continue;
1643 };
1644 let button_settings = settings.get_button_settings(button);
1645 gamepad_buttons.analog.set(button, filtered_value.raw);
1646
1647 if button_settings.is_released(filtered_value.raw) {
1648 if gamepad_buttons.pressed(button) {
1650 processed_digital_events.write(GamepadButtonStateChangedEvent::new(
1651 gamepad,
1652 button,
1653 ButtonState::Released,
1654 ));
1655 }
1656 gamepad_buttons.digital.release(button);
1659 } else if button_settings.is_pressed(filtered_value.raw) {
1660 if !gamepad_buttons.pressed(button) {
1662 processed_digital_events.write(GamepadButtonStateChangedEvent::new(
1663 gamepad,
1664 button,
1665 ButtonState::Pressed,
1666 ));
1667 }
1668 gamepad_buttons.digital.press(button);
1669 };
1670
1671 let button_state = if gamepad_buttons.digital.pressed(button) {
1672 ButtonState::Pressed
1673 } else {
1674 ButtonState::Released
1675 };
1676 let send_event = GamepadButtonChangedEvent::new(
1677 gamepad,
1678 button,
1679 button_state,
1680 filtered_value.scaled.to_f32(),
1681 );
1682 processed_analog_events.write(send_event);
1683 processed_events.write(GamepadEvent::from(send_event));
1684 }
1685 }
1686 }
1687}
1688
1689#[derive(Clone, Copy, Debug, PartialEq)]
1691#[cfg_attr(
1692 feature = "bevy_reflect",
1693 derive(Reflect),
1694 reflect(Debug, PartialEq, Clone)
1695)]
1696pub struct GamepadRumbleIntensity {
1697 pub strong_motor: f32,
1704 pub weak_motor: f32,
1711}
1712
1713impl GamepadRumbleIntensity {
1714 pub const MAX: Self = GamepadRumbleIntensity {
1716 strong_motor: 1.0,
1717 weak_motor: 1.0,
1718 };
1719
1720 pub const WEAK_MAX: Self = GamepadRumbleIntensity {
1722 strong_motor: 0.0,
1723 weak_motor: 1.0,
1724 };
1725
1726 pub const STRONG_MAX: Self = GamepadRumbleIntensity {
1728 strong_motor: 1.0,
1729 weak_motor: 0.0,
1730 };
1731
1732 pub const fn weak_motor(intensity: f32) -> Self {
1736 Self {
1737 weak_motor: intensity,
1738 strong_motor: 0.0,
1739 }
1740 }
1741
1742 pub const fn strong_motor(intensity: f32) -> Self {
1746 Self {
1747 strong_motor: intensity,
1748 weak_motor: 0.0,
1749 }
1750 }
1751}
1752
1753#[doc(alias = "haptic feedback")]
1779#[doc(alias = "force feedback")]
1780#[doc(alias = "vibration")]
1781#[doc(alias = "vibrate")]
1782#[derive(Message, Clone)]
1783#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Clone, Message))]
1784pub enum GamepadRumbleRequest {
1785 Add {
1796 duration: Duration,
1798 intensity: GamepadRumbleIntensity,
1800 gamepad: Entity,
1802 },
1803 Stop {
1805 gamepad: Entity,
1807 },
1808}
1809
1810impl GamepadRumbleRequest {
1811 pub fn gamepad(&self) -> Entity {
1813 match self {
1814 Self::Add { gamepad, .. } | Self::Stop { gamepad } => *gamepad,
1815 }
1816 }
1817}
1818
1819#[cfg(test)]
1820mod tests {
1821 use super::{
1822 gamepad_connection_system, gamepad_event_processing_system, AxisSettings,
1823 AxisSettingsError, ButtonAxisSettings, ButtonSettings, ButtonSettingsError, Gamepad,
1824 GamepadAxis, GamepadAxisChangedEvent, GamepadButton, GamepadButtonChangedEvent,
1825 GamepadButtonStateChangedEvent,
1826 GamepadConnection::{Connected, Disconnected},
1827 GamepadConnectionEvent, GamepadEvent, GamepadSettings, RawGamepadAxisChangedEvent,
1828 RawGamepadButtonChangedEvent, RawGamepadEvent,
1829 };
1830 use crate::ButtonState;
1831 use alloc::string::ToString;
1832 use bevy_app::{App, PreUpdate};
1833 use bevy_ecs::entity::Entity;
1834 use bevy_ecs::message::Messages;
1835 use bevy_ecs::schedule::IntoScheduleConfigs;
1836
1837 fn test_button_axis_settings_filter(
1838 settings: ButtonAxisSettings,
1839 new_raw_value: f32,
1840 old_raw_value: Option<f32>,
1841 expected: Option<f32>,
1842 ) {
1843 let actual = settings
1844 .filter(new_raw_value, old_raw_value)
1845 .map(|f| f.scaled.to_f32());
1846 assert_eq!(
1847 expected, actual,
1848 "Testing filtering for {settings:?} with new_raw_value = {new_raw_value:?}, old_raw_value = {old_raw_value:?}",
1849 );
1850 }
1851
1852 #[test]
1853 fn test_button_axis_settings_default_filter() {
1854 let cases = [
1855 (1.0, None, Some(1.0)),
1857 (0.99, None, Some(1.0)),
1858 (0.96, None, Some(1.0)),
1859 (0.95, None, Some(1.0)),
1860 (0.9499, None, Some(0.9998889)),
1862 (0.84, None, Some(0.87777776)),
1863 (0.43, None, Some(0.42222223)),
1864 (0.05001, None, Some(0.000011109644)),
1865 (0.05, None, Some(0.0)),
1867 (0.04, None, Some(0.0)),
1868 (0.01, None, Some(0.0)),
1869 (0.0, None, Some(0.0)),
1870 ];
1871
1872 for (new_raw_value, old_raw_value, expected) in cases {
1873 let settings = ButtonAxisSettings::default();
1874 test_button_axis_settings_filter(settings, new_raw_value, old_raw_value, expected);
1875 }
1876 }
1877
1878 #[test]
1879 fn test_button_axis_settings_default_filter_with_old_raw_value() {
1880 let cases = [
1881 (0.43, Some(0.44001), Some(0.42222223)),
1883 (0.43, Some(0.44), None),
1884 (0.43, Some(0.43), None),
1885 (0.43, Some(0.41999), Some(0.42222223)),
1886 (0.43, Some(0.17), Some(0.42222223)),
1887 (0.43, Some(0.84), Some(0.42222223)),
1888 (0.05, Some(0.055), Some(0.0)),
1889 (0.95, Some(0.945), Some(1.0)),
1890 ];
1891
1892 for (new_raw_value, old_raw_value, expected) in cases {
1893 let settings = ButtonAxisSettings::default();
1894 test_button_axis_settings_filter(settings, new_raw_value, old_raw_value, expected);
1895 }
1896 }
1897
1898 fn test_axis_settings_filter(
1899 settings: AxisSettings,
1900 new_raw_value: f32,
1901 old_raw_value: Option<f32>,
1902 expected: Option<f32>,
1903 ) {
1904 let actual = settings.filter(new_raw_value, old_raw_value);
1905 assert_eq!(
1906 expected, actual.map(|f| f.scaled.to_f32()),
1907 "Testing filtering for {settings:?} with new_raw_value = {new_raw_value:?}, old_raw_value = {old_raw_value:?}",
1908 );
1909 }
1910
1911 #[test]
1912 fn test_axis_settings_default_filter() {
1913 let cases = [
1915 (1.0, Some(1.0)),
1917 (0.99, Some(1.0)),
1918 (0.96, Some(1.0)),
1919 (0.95, Some(1.0)),
1920 (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)),
1929 (0.04, Some(0.0)),
1930 (0.01, Some(0.0)),
1931 (0.0, Some(0.0)),
1932 (-1.0, Some(-1.0)),
1935 (-0.99, Some(-1.0)),
1936 (-0.96, Some(-1.0)),
1937 (-0.95, Some(-1.0)),
1938 (-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)),
1945 (-0.04, Some(0.0)),
1946 (-0.01, Some(0.0)),
1947 ];
1948
1949 for (new_raw_value, expected) in cases {
1950 let settings = AxisSettings::new(-0.95, -0.05, 0.05, 0.95, 0.01).unwrap();
1951 test_axis_settings_filter(settings, new_raw_value, None, expected);
1952 }
1953 }
1954
1955 #[test]
1956 fn test_axis_settings_default_filter_with_old_raw_values() {
1957 let threshold = 0.01;
1958 let cases = [
1961 (0.43, Some(0.43 + threshold * 1.1), Some(0.42222223)),
1963 (0.43, Some(0.43 - threshold * 1.1), Some(0.42222223)),
1965 (0.43, Some(0.43 + threshold * 0.9), None),
1967 (0.43, Some(0.43 - threshold * 0.9), None),
1969 (-0.43, Some(-0.43 + threshold * 1.1), Some(-0.42222226)),
1971 (-0.43, Some(-0.43 - threshold * 1.1), Some(-0.42222226)),
1973 (-0.43, Some(-0.43 + threshold * 0.9), None),
1975 (-0.43, Some(-0.43 - threshold * 0.9), None),
1977 (0.05, Some(0.0), None),
1979 (0.06, Some(0.0), Some(0.0111111095)),
1980 (-0.05, Some(0.0), None),
1982 (-0.06, Some(0.0), Some(-0.011111081)),
1983 (0.95, Some(1.0), None),
1985 (0.94, Some(1.0), Some(0.9888889)),
1986 (-0.95, Some(-1.0), None),
1988 (-0.94, Some(-1.0), Some(-0.9888889)),
1989 ];
1990
1991 for (new_raw_value, old_raw_value, expected) in cases {
1992 let settings = AxisSettings::new(-0.95, -0.05, 0.05, 0.95, threshold).unwrap();
1993 test_axis_settings_filter(settings, new_raw_value, old_raw_value, expected);
1994 }
1995 }
1996
1997 #[test]
1998 fn test_button_settings_default_is_pressed() {
1999 let cases = [
2000 (1.0, true),
2001 (0.95, true),
2002 (0.9, true),
2003 (0.8, true),
2004 (0.75, true),
2005 (0.7, false),
2006 (0.65, false),
2007 (0.5, false),
2008 (0.0, false),
2009 ];
2010
2011 for (value, expected) in cases {
2012 let settings = ButtonSettings::default();
2013 let actual = settings.is_pressed(value);
2014
2015 assert_eq!(
2016 expected, actual,
2017 "testing ButtonSettings::is_pressed() for value: {value}",
2018 );
2019 }
2020 }
2021
2022 #[test]
2023 fn test_button_settings_default_is_released() {
2024 let cases = [
2025 (1.0, false),
2026 (0.95, false),
2027 (0.9, false),
2028 (0.8, false),
2029 (0.75, false),
2030 (0.7, false),
2031 (0.65, true),
2032 (0.5, true),
2033 (0.0, true),
2034 ];
2035
2036 for (value, expected) in cases {
2037 let settings = ButtonSettings::default();
2038 let actual = settings.is_released(value);
2039
2040 assert_eq!(
2041 expected, actual,
2042 "testing ButtonSettings::is_released() for value: {value}",
2043 );
2044 }
2045 }
2046
2047 #[test]
2048 fn test_new_button_settings_given_valid_parameters() {
2049 let cases = [
2050 (1.0, 0.0),
2051 (1.0, 1.0),
2052 (1.0, 0.9),
2053 (0.9, 0.9),
2054 (0.9, 0.0),
2055 (0.0, 0.0),
2056 ];
2057
2058 for (press_threshold, release_threshold) in cases {
2059 let bs = ButtonSettings::new(press_threshold, release_threshold);
2060 match bs {
2061 Ok(button_settings) => {
2062 assert_eq!(button_settings.press_threshold, press_threshold);
2063 assert_eq!(button_settings.release_threshold, release_threshold);
2064 }
2065 Err(_) => {
2066 panic!(
2067 "ButtonSettings::new({press_threshold}, {release_threshold}) should be valid"
2068 );
2069 }
2070 }
2071 }
2072 }
2073
2074 #[test]
2075 fn test_new_button_settings_given_invalid_parameters() {
2076 let cases = [
2077 (1.1, 0.0),
2078 (1.1, 1.0),
2079 (1.0, 1.1),
2080 (-1.0, 0.9),
2081 (-1.0, 0.0),
2082 (-1.0, -0.4),
2083 (0.9, 1.0),
2084 (0.0, 0.1),
2085 ];
2086
2087 for (press_threshold, release_threshold) in cases {
2088 let bs = ButtonSettings::new(press_threshold, release_threshold);
2089 match bs {
2090 Ok(_) => {
2091 panic!(
2092 "ButtonSettings::new({press_threshold}, {release_threshold}) should be invalid"
2093 );
2094 }
2095 Err(err_code) => match err_code {
2096 ButtonSettingsError::PressThresholdOutOfRange(_press_threshold) => {}
2097 ButtonSettingsError::ReleaseThresholdGreaterThanPressThreshold {
2098 press_threshold: _press_threshold,
2099 release_threshold: _release_threshold,
2100 } => {}
2101 ButtonSettingsError::ReleaseThresholdOutOfRange(_release_threshold) => {}
2102 },
2103 }
2104 }
2105 }
2106
2107 #[test]
2108 fn test_try_out_of_range_axis_settings() {
2109 let mut axis_settings = AxisSettings::default();
2110 assert_eq!(
2111 AxisSettings::new(-0.95, -0.05, 0.05, 0.95, 0.001),
2112 Ok(AxisSettings {
2113 livezone_lowerbound: -0.95,
2114 deadzone_lowerbound: -0.05,
2115 deadzone_upperbound: 0.05,
2116 livezone_upperbound: 0.95,
2117 threshold: 0.001,
2118 })
2119 );
2120 assert_eq!(
2121 Err(AxisSettingsError::LiveZoneLowerBoundOutOfRange(-2.0)),
2122 axis_settings.try_set_livezone_lowerbound(-2.0)
2123 );
2124 assert_eq!(
2125 Err(AxisSettingsError::LiveZoneLowerBoundOutOfRange(0.1)),
2126 axis_settings.try_set_livezone_lowerbound(0.1)
2127 );
2128 assert_eq!(
2129 Err(AxisSettingsError::DeadZoneLowerBoundOutOfRange(-2.0)),
2130 axis_settings.try_set_deadzone_lowerbound(-2.0)
2131 );
2132 assert_eq!(
2133 Err(AxisSettingsError::DeadZoneLowerBoundOutOfRange(0.1)),
2134 axis_settings.try_set_deadzone_lowerbound(0.1)
2135 );
2136
2137 assert_eq!(
2138 Err(AxisSettingsError::DeadZoneUpperBoundOutOfRange(-0.1)),
2139 axis_settings.try_set_deadzone_upperbound(-0.1)
2140 );
2141 assert_eq!(
2142 Err(AxisSettingsError::DeadZoneUpperBoundOutOfRange(1.1)),
2143 axis_settings.try_set_deadzone_upperbound(1.1)
2144 );
2145 assert_eq!(
2146 Err(AxisSettingsError::LiveZoneUpperBoundOutOfRange(-0.1)),
2147 axis_settings.try_set_livezone_upperbound(-0.1)
2148 );
2149 assert_eq!(
2150 Err(AxisSettingsError::LiveZoneUpperBoundOutOfRange(1.1)),
2151 axis_settings.try_set_livezone_upperbound(1.1)
2152 );
2153
2154 axis_settings.set_livezone_lowerbound(-0.7);
2155 axis_settings.set_deadzone_lowerbound(-0.3);
2156 assert_eq!(
2157 Err(
2158 AxisSettingsError::LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
2159 livezone_lowerbound: -0.1,
2160 deadzone_lowerbound: -0.3,
2161 }
2162 ),
2163 axis_settings.try_set_livezone_lowerbound(-0.1)
2164 );
2165 assert_eq!(
2166 Err(
2167 AxisSettingsError::LiveZoneLowerBoundGreaterThanDeadZoneLowerBound {
2168 livezone_lowerbound: -0.7,
2169 deadzone_lowerbound: -0.9
2170 }
2171 ),
2172 axis_settings.try_set_deadzone_lowerbound(-0.9)
2173 );
2174
2175 axis_settings.set_deadzone_upperbound(0.3);
2176 axis_settings.set_livezone_upperbound(0.7);
2177 assert_eq!(
2178 Err(
2179 AxisSettingsError::DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
2180 deadzone_upperbound: 0.8,
2181 livezone_upperbound: 0.7
2182 }
2183 ),
2184 axis_settings.try_set_deadzone_upperbound(0.8)
2185 );
2186 assert_eq!(
2187 Err(
2188 AxisSettingsError::DeadZoneUpperBoundGreaterThanLiveZoneUpperBound {
2189 deadzone_upperbound: 0.3,
2190 livezone_upperbound: 0.1
2191 }
2192 ),
2193 axis_settings.try_set_livezone_upperbound(0.1)
2194 );
2195 }
2196
2197 struct TestContext {
2198 pub app: App,
2199 }
2200
2201 impl TestContext {
2202 pub fn new() -> Self {
2203 let mut app = App::new();
2204 app.add_systems(
2205 PreUpdate,
2206 (
2207 gamepad_connection_system,
2208 gamepad_event_processing_system.after(gamepad_connection_system),
2209 ),
2210 )
2211 .add_message::<GamepadEvent>()
2212 .add_message::<GamepadConnectionEvent>()
2213 .add_message::<RawGamepadButtonChangedEvent>()
2214 .add_message::<GamepadButtonChangedEvent>()
2215 .add_message::<GamepadButtonStateChangedEvent>()
2216 .add_message::<GamepadAxisChangedEvent>()
2217 .add_message::<RawGamepadAxisChangedEvent>()
2218 .add_message::<RawGamepadEvent>();
2219 Self { app }
2220 }
2221
2222 pub fn update(&mut self) {
2223 self.app.update();
2224 }
2225
2226 pub fn send_gamepad_connection_event(&mut self, gamepad: Option<Entity>) -> Entity {
2227 let gamepad = gamepad.unwrap_or_else(|| self.app.world_mut().spawn_empty().id());
2228 self.app
2229 .world_mut()
2230 .resource_mut::<Messages<GamepadConnectionEvent>>()
2231 .write(GamepadConnectionEvent::new(
2232 gamepad,
2233 Connected {
2234 name: "Test gamepad".to_string(),
2235 vendor_id: None,
2236 product_id: None,
2237 },
2238 ));
2239 gamepad
2240 }
2241
2242 pub fn send_gamepad_disconnection_event(&mut self, gamepad: Entity) {
2243 self.app
2244 .world_mut()
2245 .resource_mut::<Messages<GamepadConnectionEvent>>()
2246 .write(GamepadConnectionEvent::new(gamepad, Disconnected));
2247 }
2248
2249 pub fn send_raw_gamepad_event(&mut self, event: RawGamepadEvent) {
2250 self.app
2251 .world_mut()
2252 .resource_mut::<Messages<RawGamepadEvent>>()
2253 .write(event);
2254 }
2255
2256 pub fn send_raw_gamepad_event_batch(
2257 &mut self,
2258 events: impl IntoIterator<Item = RawGamepadEvent>,
2259 ) {
2260 self.app
2261 .world_mut()
2262 .resource_mut::<Messages<RawGamepadEvent>>()
2263 .write_batch(events);
2264 }
2265 }
2266
2267 #[test]
2268 fn connection_event() {
2269 let mut ctx = TestContext::new();
2270 assert_eq!(
2271 ctx.app
2272 .world_mut()
2273 .query::<&Gamepad>()
2274 .iter(ctx.app.world())
2275 .len(),
2276 0
2277 );
2278 ctx.send_gamepad_connection_event(None);
2279 ctx.update();
2280 assert_eq!(
2281 ctx.app
2282 .world_mut()
2283 .query::<(&Gamepad, &GamepadSettings)>()
2284 .iter(ctx.app.world())
2285 .len(),
2286 1
2287 );
2288 }
2289
2290 #[test]
2291 fn disconnection_event() {
2292 let mut ctx = TestContext::new();
2293 assert_eq!(
2294 ctx.app
2295 .world_mut()
2296 .query::<&Gamepad>()
2297 .iter(ctx.app.world())
2298 .len(),
2299 0
2300 );
2301 let entity = ctx.send_gamepad_connection_event(None);
2302 ctx.update();
2303 assert_eq!(
2304 ctx.app
2305 .world_mut()
2306 .query::<(&Gamepad, &GamepadSettings)>()
2307 .iter(ctx.app.world())
2308 .len(),
2309 1
2310 );
2311 ctx.send_gamepad_disconnection_event(entity);
2312 ctx.update();
2313 assert!(ctx
2315 .app
2316 .world_mut()
2317 .query::<&Gamepad>()
2318 .get(ctx.app.world(), entity)
2319 .is_err());
2320 assert!(ctx
2322 .app
2323 .world_mut()
2324 .query::<&GamepadSettings>()
2325 .get(ctx.app.world(), entity)
2326 .is_ok());
2327
2328 ctx.send_gamepad_disconnection_event(entity);
2330 ctx.update();
2331 assert!(ctx
2332 .app
2333 .world_mut()
2334 .query::<&Gamepad>()
2335 .get(ctx.app.world(), entity)
2336 .is_err());
2337 assert!(ctx
2338 .app
2339 .world_mut()
2340 .query::<&GamepadSettings>()
2341 .get(ctx.app.world(), entity)
2342 .is_ok());
2343 }
2344
2345 #[test]
2346 fn connection_disconnection_frame_event() {
2347 let mut ctx = TestContext::new();
2348 assert_eq!(
2349 ctx.app
2350 .world_mut()
2351 .query::<&Gamepad>()
2352 .iter(ctx.app.world())
2353 .len(),
2354 0
2355 );
2356 let entity = ctx.send_gamepad_connection_event(None);
2357 ctx.send_gamepad_disconnection_event(entity);
2358 ctx.update();
2359 assert!(ctx
2361 .app
2362 .world_mut()
2363 .query::<&Gamepad>()
2364 .get(ctx.app.world(), entity)
2365 .is_err());
2366 assert!(ctx
2368 .app
2369 .world_mut()
2370 .query::<&GamepadSettings>()
2371 .get(ctx.app.world(), entity)
2372 .is_ok());
2373 }
2374
2375 #[test]
2376 fn reconnection_event() {
2377 let button_settings = ButtonSettings::new(0.7, 0.2).expect("correct parameters");
2378 let mut ctx = TestContext::new();
2379 assert_eq!(
2380 ctx.app
2381 .world_mut()
2382 .query::<&Gamepad>()
2383 .iter(ctx.app.world())
2384 .len(),
2385 0
2386 );
2387 let entity = ctx.send_gamepad_connection_event(None);
2388 ctx.update();
2389 let mut settings = ctx
2390 .app
2391 .world_mut()
2392 .query::<&mut GamepadSettings>()
2393 .get_mut(ctx.app.world_mut(), entity)
2394 .expect("be alive");
2395 assert_ne!(settings.default_button_settings, button_settings);
2396 settings.default_button_settings = button_settings.clone();
2397 ctx.send_gamepad_disconnection_event(entity);
2398 ctx.update();
2399 assert_eq!(
2400 ctx.app
2401 .world_mut()
2402 .query::<&Gamepad>()
2403 .iter(ctx.app.world())
2404 .len(),
2405 0
2406 );
2407 ctx.send_gamepad_connection_event(Some(entity));
2408 ctx.update();
2409 let settings = ctx
2410 .app
2411 .world_mut()
2412 .query::<&GamepadSettings>()
2413 .get(ctx.app.world(), entity)
2414 .expect("be alive");
2415 assert_eq!(settings.default_button_settings, button_settings);
2416 }
2417
2418 #[test]
2419 fn reconnection_same_frame_event() {
2420 let mut ctx = TestContext::new();
2421 assert_eq!(
2422 ctx.app
2423 .world_mut()
2424 .query::<&Gamepad>()
2425 .iter(ctx.app.world())
2426 .len(),
2427 0
2428 );
2429 let entity = ctx.send_gamepad_connection_event(None);
2430 ctx.send_gamepad_disconnection_event(entity);
2431 ctx.update();
2432 assert_eq!(
2433 ctx.app
2434 .world_mut()
2435 .query::<&Gamepad>()
2436 .iter(ctx.app.world())
2437 .len(),
2438 0
2439 );
2440 assert!(ctx
2441 .app
2442 .world_mut()
2443 .query::<(Entity, &GamepadSettings)>()
2444 .get(ctx.app.world(), entity)
2445 .is_ok());
2446 }
2447
2448 #[test]
2449 fn gamepad_axis_valid() {
2450 let mut ctx = TestContext::new();
2451
2452 let entity = ctx.send_gamepad_connection_event(None);
2454 ctx.app
2455 .world_mut()
2456 .resource_mut::<Messages<RawGamepadEvent>>()
2457 .write_batch([
2458 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2459 entity,
2460 GamepadAxis::LeftStickY,
2461 0.5,
2462 )),
2463 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2464 entity,
2465 GamepadAxis::RightStickX,
2466 0.6,
2467 )),
2468 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2469 entity,
2470 GamepadAxis::RightZ,
2471 -0.4,
2472 )),
2473 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2474 entity,
2475 GamepadAxis::RightStickY,
2476 -0.8,
2477 )),
2478 ]);
2479 ctx.update();
2480 assert_eq!(
2481 ctx.app
2482 .world()
2483 .resource::<Messages<GamepadAxisChangedEvent>>()
2484 .len(),
2485 4
2486 );
2487 }
2488
2489 #[test]
2490 fn gamepad_axis_threshold_filter() {
2491 let mut ctx = TestContext::new();
2492
2493 let entity = ctx.send_gamepad_connection_event(None);
2495 let settings = GamepadSettings::default().default_axis_settings;
2496 let base_value = 0.5;
2498 let events = [
2499 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2501 entity,
2502 GamepadAxis::LeftStickX,
2503 base_value,
2504 )),
2505 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2507 entity,
2508 GamepadAxis::LeftStickX,
2509 base_value + settings.threshold - 0.01,
2510 )),
2511 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2513 entity,
2514 GamepadAxis::LeftStickX,
2515 base_value + settings.threshold + 0.01,
2516 )),
2517 ];
2518 ctx.app
2519 .world_mut()
2520 .resource_mut::<Messages<RawGamepadEvent>>()
2521 .write_batch(events);
2522 ctx.update();
2523 assert_eq!(
2524 ctx.app
2525 .world()
2526 .resource::<Messages<GamepadAxisChangedEvent>>()
2527 .len(),
2528 2
2529 );
2530 }
2531
2532 #[test]
2533 fn gamepad_axis_deadzone_filter() {
2534 let mut ctx = TestContext::new();
2535
2536 let entity = ctx.send_gamepad_connection_event(None);
2538 let settings = GamepadSettings::default().default_axis_settings;
2539
2540 let events = [
2542 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2544 entity,
2545 GamepadAxis::LeftStickX,
2546 settings.deadzone_upperbound - 0.01,
2547 )),
2548 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2550 entity,
2551 GamepadAxis::LeftStickX,
2552 settings.deadzone_lowerbound + 0.01,
2553 )),
2554 ];
2555 ctx.app
2556 .world_mut()
2557 .resource_mut::<Messages<RawGamepadEvent>>()
2558 .write_batch(events);
2559 ctx.update();
2560 assert_eq!(
2561 ctx.app
2562 .world()
2563 .resource::<Messages<GamepadAxisChangedEvent>>()
2564 .len(),
2565 0
2566 );
2567 }
2568
2569 #[test]
2570 fn gamepad_axis_deadzone_rounded() {
2571 let mut ctx = TestContext::new();
2572
2573 let entity = ctx.send_gamepad_connection_event(None);
2575 let settings = GamepadSettings::default().default_axis_settings;
2576
2577 let events = [
2579 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2580 entity,
2581 GamepadAxis::LeftStickX,
2582 1.0,
2583 )),
2584 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2586 entity,
2587 GamepadAxis::LeftStickX,
2588 settings.deadzone_upperbound - 0.01,
2589 )),
2590 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2591 entity,
2592 GamepadAxis::LeftStickX,
2593 1.0,
2594 )),
2595 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2597 entity,
2598 GamepadAxis::LeftStickX,
2599 settings.deadzone_lowerbound + 0.01,
2600 )),
2601 ];
2602 let results = [1.0, 0.0, 1.0, 0.0];
2603 ctx.app
2604 .world_mut()
2605 .resource_mut::<Messages<RawGamepadEvent>>()
2606 .write_batch(events);
2607 ctx.update();
2608
2609 let events = ctx
2610 .app
2611 .world()
2612 .resource::<Messages<GamepadAxisChangedEvent>>();
2613 let mut event_reader = events.get_cursor();
2614 for (event, result) in event_reader.read(events).zip(results) {
2615 assert_eq!(event.value, result);
2616 }
2617 assert_eq!(
2618 ctx.app
2619 .world()
2620 .resource::<Messages<GamepadAxisChangedEvent>>()
2621 .len(),
2622 4
2623 );
2624 }
2625
2626 #[test]
2627 fn gamepad_axis_livezone_filter() {
2628 let mut ctx = TestContext::new();
2629
2630 let entity = ctx.send_gamepad_connection_event(None);
2632 let settings = GamepadSettings::default().default_axis_settings;
2633
2634 let events = [
2636 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2637 entity,
2638 GamepadAxis::LeftStickX,
2639 1.0,
2640 )),
2641 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2643 entity,
2644 GamepadAxis::LeftStickX,
2645 settings.livezone_upperbound + 0.01,
2646 )),
2647 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2648 entity,
2649 GamepadAxis::LeftStickX,
2650 -1.0,
2651 )),
2652 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2654 entity,
2655 GamepadAxis::LeftStickX,
2656 settings.livezone_lowerbound - 0.01,
2657 )),
2658 ];
2659 ctx.app
2660 .world_mut()
2661 .resource_mut::<Messages<RawGamepadEvent>>()
2662 .write_batch(events);
2663 ctx.update();
2664 assert_eq!(
2665 ctx.app
2666 .world()
2667 .resource::<Messages<GamepadAxisChangedEvent>>()
2668 .len(),
2669 2
2670 );
2671 }
2672
2673 #[test]
2674 fn gamepad_axis_livezone_rounded() {
2675 let mut ctx = TestContext::new();
2676
2677 let entity = ctx.send_gamepad_connection_event(None);
2679 let settings = GamepadSettings::default().default_axis_settings;
2680
2681 let events = [
2683 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2685 entity,
2686 GamepadAxis::LeftStickX,
2687 settings.livezone_upperbound + 0.01,
2688 )),
2689 RawGamepadEvent::Axis(RawGamepadAxisChangedEvent::new(
2691 entity,
2692 GamepadAxis::LeftStickX,
2693 settings.livezone_lowerbound - 0.01,
2694 )),
2695 ];
2696 let results = [1.0, -1.0];
2697 ctx.app
2698 .world_mut()
2699 .resource_mut::<Messages<RawGamepadEvent>>()
2700 .write_batch(events);
2701 ctx.update();
2702
2703 let events = ctx
2704 .app
2705 .world()
2706 .resource::<Messages<GamepadAxisChangedEvent>>();
2707 let mut event_reader = events.get_cursor();
2708 for (event, result) in event_reader.read(events).zip(results) {
2709 assert_eq!(event.value, result);
2710 }
2711 assert_eq!(
2712 ctx.app
2713 .world()
2714 .resource::<Messages<GamepadAxisChangedEvent>>()
2715 .len(),
2716 2
2717 );
2718 }
2719
2720 #[test]
2721 fn gamepad_buttons_pressed() {
2722 let mut ctx = TestContext::new();
2723
2724 let entity = ctx.send_gamepad_connection_event(None);
2726 let digital_settings = GamepadSettings::default().default_button_settings;
2727
2728 let events = [RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2729 entity,
2730 GamepadButton::DPadDown,
2731 digital_settings.press_threshold,
2732 ))];
2733 ctx.app
2734 .world_mut()
2735 .resource_mut::<Messages<RawGamepadEvent>>()
2736 .write_batch(events);
2737 ctx.update();
2738
2739 assert_eq!(
2740 ctx.app
2741 .world()
2742 .resource::<Messages<GamepadButtonStateChangedEvent>>()
2743 .len(),
2744 1
2745 );
2746 let events = ctx
2747 .app
2748 .world()
2749 .resource::<Messages<GamepadButtonStateChangedEvent>>();
2750 let mut event_reader = events.get_cursor();
2751 for event in event_reader.read(events) {
2752 assert_eq!(event.button, GamepadButton::DPadDown);
2753 assert_eq!(event.state, ButtonState::Pressed);
2754 }
2755 assert!(ctx
2756 .app
2757 .world_mut()
2758 .query::<&Gamepad>()
2759 .get(ctx.app.world(), entity)
2760 .unwrap()
2761 .pressed(GamepadButton::DPadDown));
2762
2763 ctx.app
2764 .world_mut()
2765 .resource_mut::<Messages<GamepadButtonStateChangedEvent>>()
2766 .clear();
2767 ctx.update();
2768
2769 assert_eq!(
2770 ctx.app
2771 .world()
2772 .resource::<Messages<GamepadButtonStateChangedEvent>>()
2773 .len(),
2774 0
2775 );
2776 assert!(ctx
2777 .app
2778 .world_mut()
2779 .query::<&Gamepad>()
2780 .get(ctx.app.world(), entity)
2781 .unwrap()
2782 .pressed(GamepadButton::DPadDown));
2783 }
2784
2785 #[test]
2786 fn gamepad_buttons_just_pressed() {
2787 let mut ctx = TestContext::new();
2788
2789 let entity = ctx.send_gamepad_connection_event(None);
2791 let digital_settings = GamepadSettings::default().default_button_settings;
2792
2793 ctx.send_raw_gamepad_event(RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2794 entity,
2795 GamepadButton::DPadDown,
2796 digital_settings.press_threshold,
2797 )));
2798 ctx.update();
2799
2800 assert!(ctx
2802 .app
2803 .world_mut()
2804 .query::<&Gamepad>()
2805 .get(ctx.app.world(), entity)
2806 .unwrap()
2807 .just_pressed(GamepadButton::DPadDown));
2808 ctx.update();
2809
2810 assert!(!ctx
2812 .app
2813 .world_mut()
2814 .query::<&Gamepad>()
2815 .get(ctx.app.world(), entity)
2816 .unwrap()
2817 .just_pressed(GamepadButton::DPadDown));
2818 }
2819 #[test]
2820 fn gamepad_buttons_released() {
2821 let mut ctx = TestContext::new();
2822
2823 let entity = ctx.send_gamepad_connection_event(None);
2825 let digital_settings = GamepadSettings::default().default_button_settings;
2826
2827 ctx.send_raw_gamepad_event(RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2828 entity,
2829 GamepadButton::DPadDown,
2830 digital_settings.press_threshold,
2831 )));
2832 ctx.update();
2833
2834 ctx.app
2835 .world_mut()
2836 .resource_mut::<Messages<GamepadButtonStateChangedEvent>>()
2837 .clear();
2838 ctx.send_raw_gamepad_event(RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2839 entity,
2840 GamepadButton::DPadDown,
2841 digital_settings.release_threshold - 0.01,
2842 )));
2843 ctx.update();
2844 assert_eq!(
2845 ctx.app
2846 .world()
2847 .resource::<Messages<GamepadButtonStateChangedEvent>>()
2848 .len(),
2849 1
2850 );
2851 let events = ctx
2852 .app
2853 .world()
2854 .resource::<Messages<GamepadButtonStateChangedEvent>>();
2855 let mut event_reader = events.get_cursor();
2856 for event in event_reader.read(events) {
2857 assert_eq!(event.button, GamepadButton::DPadDown);
2858 assert_eq!(event.state, ButtonState::Released);
2859 }
2860 assert!(!ctx
2861 .app
2862 .world_mut()
2863 .query::<&Gamepad>()
2864 .get(ctx.app.world(), entity)
2865 .unwrap()
2866 .pressed(GamepadButton::DPadDown));
2867 ctx.app
2868 .world_mut()
2869 .resource_mut::<Messages<GamepadButtonStateChangedEvent>>()
2870 .clear();
2871 ctx.update();
2872
2873 assert_eq!(
2874 ctx.app
2875 .world()
2876 .resource::<Messages<GamepadButtonStateChangedEvent>>()
2877 .len(),
2878 0
2879 );
2880 }
2881
2882 #[test]
2883 fn gamepad_buttons_just_released() {
2884 let mut ctx = TestContext::new();
2885
2886 let entity = ctx.send_gamepad_connection_event(None);
2888 let digital_settings = GamepadSettings::default().default_button_settings;
2889
2890 ctx.send_raw_gamepad_event_batch([
2891 RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2892 entity,
2893 GamepadButton::DPadDown,
2894 digital_settings.press_threshold,
2895 )),
2896 RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2897 entity,
2898 GamepadButton::DPadDown,
2899 digital_settings.release_threshold - 0.01,
2900 )),
2901 ]);
2902 ctx.update();
2903
2904 assert!(ctx
2906 .app
2907 .world_mut()
2908 .query::<&Gamepad>()
2909 .get(ctx.app.world(), entity)
2910 .unwrap()
2911 .just_released(GamepadButton::DPadDown));
2912 ctx.update();
2913
2914 assert!(!ctx
2916 .app
2917 .world_mut()
2918 .query::<&Gamepad>()
2919 .get(ctx.app.world(), entity)
2920 .unwrap()
2921 .just_released(GamepadButton::DPadDown));
2922 }
2923
2924 #[test]
2925 fn gamepad_buttons_axis() {
2926 let mut ctx = TestContext::new();
2927
2928 let entity = ctx.send_gamepad_connection_event(None);
2930 let digital_settings = GamepadSettings::default().default_button_settings;
2931 let analog_settings = GamepadSettings::default().default_button_axis_settings;
2932
2933 let events = [
2935 RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2937 entity,
2938 GamepadButton::DPadDown,
2939 digital_settings.press_threshold,
2940 )),
2941 RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2943 entity,
2944 GamepadButton::DPadDown,
2945 digital_settings.release_threshold,
2946 )),
2947 RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2949 entity,
2950 GamepadButton::DPadDown,
2951 digital_settings.release_threshold - analog_settings.threshold * 1.01,
2952 )),
2953 RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2955 entity,
2956 GamepadButton::DPadDown,
2957 digital_settings.release_threshold - (analog_settings.threshold * 1.5),
2958 )),
2959 RawGamepadEvent::Button(RawGamepadButtonChangedEvent::new(
2961 entity,
2962 GamepadButton::DPadDown,
2963 digital_settings.release_threshold - (analog_settings.threshold * 2.02),
2964 )),
2965 ];
2966 ctx.send_raw_gamepad_event_batch(events);
2967 ctx.update();
2968 assert_eq!(
2969 ctx.app
2970 .world()
2971 .resource::<Messages<GamepadButtonStateChangedEvent>>()
2972 .len(),
2973 2
2974 );
2975 assert_eq!(
2976 ctx.app
2977 .world()
2978 .resource::<Messages<GamepadButtonChangedEvent>>()
2979 .len(),
2980 4
2981 );
2982 }
2983}