1use std::hash::{Hash, Hasher};
4
5use bevy::ecs::message::Messages;
6use bevy::ecs::system::lifetimeless::{Read, SQuery};
7use bevy::ecs::system::{StaticSystemParam, SystemParam, SystemState};
8use bevy::input::gamepad::{
9 GamepadInput, RawGamepadAxisChangedEvent, RawGamepadButtonChangedEvent, RawGamepadEvent,
10};
11use bevy::input::{Axis, ButtonInput};
12use bevy::math::FloatOrd;
13use bevy::prelude::{
14 Entity, Gamepad, GamepadAxis, GamepadButton, Query, Reflect, Res, ResMut, Vec2, With, World,
15};
16use leafwing_input_manager_macros::serde_typetag;
17use serde::{Deserialize, Serialize};
18
19use crate as leafwing_input_manager;
20use crate::axislike::AxisDirection;
21use crate::buttonlike::ButtonValue;
22use crate::clashing_inputs::BasicInputs;
23use crate::input_processing::{
24 AxisProcessor, DualAxisProcessor, WithAxisProcessingPipelineExt,
25 WithDualAxisProcessingPipelineExt,
26};
27use crate::user_input::UserInput;
28use crate::InputControlKind;
29
30use super::updating::{CentralInputStore, UpdatableInput};
31use super::{Axislike, Buttonlike, DualAxislike};
32
33#[must_use]
37pub fn find_gamepad(gamepads: Option<Query<Entity, With<Gamepad>>>) -> Entity {
38 match gamepads {
39 None => Entity::PLACEHOLDER,
40 Some(gamepads) => gamepads.iter().next().unwrap_or(Entity::PLACEHOLDER),
41 }
42}
43
44#[must_use]
46#[inline]
47fn read_axis_value(
48 input_store: &CentralInputStore,
49 gamepad: Entity,
50 axis: GamepadAxis,
51) -> Option<f32> {
52 let axis = SpecificGamepadAxis::new(gamepad, axis);
53 input_store.value(&axis)
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
58pub struct SpecificGamepadAxis {
59 pub gamepad: Entity,
61 pub axis: GamepadAxis,
63}
64
65impl SpecificGamepadAxis {
66 pub fn new(gamepad: Entity, axis: GamepadAxis) -> Self {
68 Self { gamepad, axis }
69 }
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
74pub struct SpecificGamepadButton {
75 pub gamepad: Entity,
77 pub button: GamepadButton,
79}
80
81impl SpecificGamepadButton {
82 pub fn new(gamepad: Entity, button: GamepadButton) -> Self {
85 Self { gamepad, button }
86 }
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Reflect, Serialize, Deserialize)]
117#[must_use]
118pub struct GamepadControlDirection {
119 pub axis: GamepadAxis,
121
122 pub direction: AxisDirection,
124
125 pub threshold: f32,
128}
129
130impl GamepadControlDirection {
131 #[inline]
133 pub const fn negative(axis: GamepadAxis) -> Self {
134 Self {
135 axis,
136 direction: AxisDirection::Negative,
137 threshold: 0.0,
138 }
139 }
140
141 #[inline]
143 pub const fn positive(axis: GamepadAxis) -> Self {
144 Self {
145 axis,
146 direction: AxisDirection::Positive,
147 threshold: 0.0,
148 }
149 }
150
151 #[inline]
161 pub fn threshold(mut self, threshold: f32) -> Self {
162 assert!(threshold >= 0.0);
163 self.threshold = threshold;
164 self
165 }
166
167 pub const LEFT_UP: Self = Self::positive(GamepadAxis::LeftStickY);
169
170 pub const LEFT_DOWN: Self = Self::negative(GamepadAxis::LeftStickY);
172
173 pub const LEFT_LEFT: Self = Self::negative(GamepadAxis::LeftStickX);
175
176 pub const LEFT_RIGHT: Self = Self::positive(GamepadAxis::LeftStickX);
178
179 pub const RIGHT_UP: Self = Self::positive(GamepadAxis::RightStickY);
181
182 pub const RIGHT_DOWN: Self = Self::negative(GamepadAxis::RightStickY);
184
185 pub const RIGHT_LEFT: Self = Self::negative(GamepadAxis::RightStickX);
187
188 pub const RIGHT_RIGHT: Self = Self::positive(GamepadAxis::RightStickX);
190}
191
192impl UserInput for GamepadControlDirection {
193 #[inline]
195 fn kind(&self) -> InputControlKind {
196 InputControlKind::Button
197 }
198
199 #[inline]
201 fn decompose(&self) -> BasicInputs {
202 BasicInputs::Simple(Box::new((*self).threshold(0.0)))
203 }
204}
205
206#[serde_typetag]
207impl Buttonlike for GamepadControlDirection {
208 #[inline]
210 fn get_pressed(&self, input_store: &CentralInputStore, gamepad: Entity) -> Option<bool> {
211 read_axis_value(input_store, gamepad, self.axis)
212 .map(|value| self.direction.is_active(value, self.threshold))
213 }
214
215 fn press_as_gamepad(&self, world: &mut World, gamepad: Option<Entity>) {
217 let mut query_state = SystemState::<Query<Entity, With<Gamepad>>>::new(world);
218 let query = query_state.get(world);
219 let gamepad = gamepad.unwrap_or(find_gamepad(Some(query)));
220
221 let message = RawGamepadEvent::Axis(RawGamepadAxisChangedEvent {
222 gamepad,
223 axis: self.axis,
224 value: self.direction.full_active_value(),
225 });
226 world
227 .resource_mut::<Messages<RawGamepadEvent>>()
228 .write(message);
229 }
230
231 fn release_as_gamepad(&self, world: &mut World, gamepad: Option<Entity>) {
233 let mut query_state = SystemState::<Query<Entity, With<Gamepad>>>::new(world);
234 let query = query_state.get(world);
235 let gamepad = gamepad.unwrap_or(find_gamepad(Some(query)));
236
237 let message = RawGamepadEvent::Axis(RawGamepadAxisChangedEvent {
238 gamepad,
239 axis: self.axis,
240 value: 0.0,
241 });
242 world
243 .resource_mut::<Messages<RawGamepadEvent>>()
244 .write(message);
245 }
246
247 fn set_value_as_gamepad(&self, world: &mut World, value: f32, gamepad: Option<Entity>) {
248 if value > 0.0 {
249 self.press_as_gamepad(world, gamepad);
250 } else {
251 self.release_as_gamepad(world, gamepad);
252 }
253 }
254}
255
256impl Eq for GamepadControlDirection {}
257
258impl Hash for GamepadControlDirection {
259 fn hash<H: Hasher>(&self, state: &mut H) {
260 self.axis.hash(state);
261 self.direction.hash(state);
262 FloatOrd(self.threshold).hash(state);
263 }
264}
265
266impl UpdatableInput for GamepadAxis {
267 type SourceData = SQuery<(Entity, Read<Gamepad>)>;
268
269 fn compute(
270 mut central_input_store: ResMut<CentralInputStore>,
271 source_data: StaticSystemParam<Self::SourceData>,
272 ) {
273 for (gamepad_entity, gamepad) in source_data.iter() {
274 for input in gamepad.get_analog_axes() {
275 let GamepadInput::Axis(axis) = input else {
276 continue;
277 };
278 let value = gamepad.get(*axis).unwrap_or_default();
279 central_input_store.update_axislike(
280 SpecificGamepadAxis {
281 gamepad: gamepad_entity,
282 axis: *axis,
283 },
284 value,
285 );
286 central_input_store.update_axislike(*axis, value);
287 }
288 }
289 }
290}
291
292impl UserInput for GamepadAxis {
293 fn kind(&self) -> InputControlKind {
294 InputControlKind::Axis
295 }
296
297 fn decompose(&self) -> BasicInputs {
298 BasicInputs::Composite(vec![
299 Box::new(GamepadControlDirection::negative(*self)),
300 Box::new(GamepadControlDirection::positive(*self)),
301 ])
302 }
303}
304
305#[serde_typetag]
306impl Axislike for GamepadAxis {
307 fn get_value(&self, input_store: &CentralInputStore, gamepad: Entity) -> Option<f32> {
308 read_axis_value(input_store, gamepad, *self)
309 }
310}
311
312impl UserInput for SpecificGamepadAxis {
316 fn kind(&self) -> InputControlKind {
317 InputControlKind::Axis
318 }
319
320 fn decompose(&self) -> BasicInputs {
321 BasicInputs::Composite(vec![
322 Box::new(GamepadControlDirection::negative(self.axis)),
323 Box::new(GamepadControlDirection::positive(self.axis)),
324 ])
325 }
326}
327
328#[serde_typetag]
329impl Axislike for SpecificGamepadAxis {
330 fn get_value(&self, input_store: &CentralInputStore, gamepad: Entity) -> Option<f32> {
331 read_axis_value(input_store, gamepad, self.axis)
332 }
333}
334
335#[derive(Debug, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
366#[must_use]
367pub struct GamepadControlAxis {
368 pub axis: GamepadAxis,
370
371 pub processors: Vec<AxisProcessor>,
373}
374
375impl GamepadControlAxis {
376 #[inline]
379 pub const fn new(axis: GamepadAxis) -> Self {
380 Self {
381 axis,
382 processors: Vec::new(),
383 }
384 }
385
386 pub const LEFT_X: Self = Self::new(GamepadAxis::LeftStickX);
389
390 pub const LEFT_Y: Self = Self::new(GamepadAxis::LeftStickY);
393
394 pub const LEFT_Z: Self = Self::new(GamepadAxis::LeftZ);
396
397 pub const RIGHT_X: Self = Self::new(GamepadAxis::RightStickX);
400
401 pub const RIGHT_Y: Self = Self::new(GamepadAxis::RightStickY);
404
405 pub const RIGHT_Z: Self = Self::new(GamepadAxis::RightZ);
407}
408
409impl UserInput for GamepadControlAxis {
410 #[inline]
412 fn kind(&self) -> InputControlKind {
413 InputControlKind::Axis
414 }
415
416 #[inline]
418 fn decompose(&self) -> BasicInputs {
419 BasicInputs::Composite(vec![
420 Box::new(GamepadControlDirection::negative(self.axis)),
421 Box::new(GamepadControlDirection::positive(self.axis)),
422 ])
423 }
424}
425
426#[serde_typetag]
427impl Axislike for GamepadControlAxis {
428 #[inline]
430 fn get_value(&self, input_store: &CentralInputStore, gamepad: Entity) -> Option<f32> {
431 read_axis_value(input_store, gamepad, self.axis).map(|value| {
432 self.processors
433 .iter()
434 .fold(value, |value, processor| processor.process(value))
435 })
436 }
437
438 fn set_value_as_gamepad(&self, world: &mut World, value: f32, gamepad: Option<Entity>) {
440 let mut query_state = SystemState::<Query<Entity, With<Gamepad>>>::new(world);
441 let query = query_state.get(world);
442 let gamepad = gamepad.unwrap_or(find_gamepad(Some(query)));
443
444 let message = RawGamepadEvent::Axis(RawGamepadAxisChangedEvent {
445 gamepad,
446 axis: self.axis,
447 value,
448 });
449 world
450 .resource_mut::<Messages<RawGamepadEvent>>()
451 .write(message);
452 }
453}
454
455impl WithAxisProcessingPipelineExt for GamepadControlAxis {
456 #[inline]
457 fn reset_processing_pipeline(mut self) -> Self {
458 self.processors.clear();
459 self
460 }
461
462 #[inline]
463 fn replace_processing_pipeline(
464 mut self,
465 processors: impl IntoIterator<Item = AxisProcessor>,
466 ) -> Self {
467 self.processors = processors.into_iter().collect();
468 self
469 }
470
471 #[inline]
472 fn with_processor(mut self, processor: impl Into<AxisProcessor>) -> Self {
473 self.processors.push(processor.into());
474 self
475 }
476}
477
478#[derive(Debug, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
509#[must_use]
510pub struct GamepadStick {
511 pub x: GamepadAxis,
513
514 pub y: GamepadAxis,
516
517 pub processors: Vec<DualAxisProcessor>,
519}
520
521impl GamepadStick {
522 pub const LEFT: Self = Self {
524 x: GamepadAxis::LeftStickX,
525 y: GamepadAxis::LeftStickY,
526 processors: Vec::new(),
527 };
528
529 pub const RIGHT: Self = Self {
531 x: GamepadAxis::RightStickX,
532 y: GamepadAxis::RightStickY,
533 processors: Vec::new(),
534 };
535}
536
537impl UserInput for GamepadStick {
538 #[inline]
540 fn kind(&self) -> InputControlKind {
541 InputControlKind::DualAxis
542 }
543
544 #[inline]
546 fn decompose(&self) -> BasicInputs {
547 BasicInputs::Composite(vec![
548 Box::new(GamepadControlDirection::negative(self.x)),
549 Box::new(GamepadControlDirection::positive(self.x)),
550 Box::new(GamepadControlDirection::negative(self.y)),
551 Box::new(GamepadControlDirection::positive(self.y)),
552 ])
553 }
554}
555
556#[serde_typetag]
557impl DualAxislike for GamepadStick {
558 #[inline]
560 fn get_axis_pair(&self, input_store: &CentralInputStore, gamepad: Entity) -> Option<Vec2> {
561 let x = read_axis_value(input_store, gamepad, self.x)?;
562 let y = read_axis_value(input_store, gamepad, self.y)?;
563 Some(
564 self.processors
565 .iter()
566 .fold(Vec2::new(x, y), |value, processor| processor.process(value)),
567 )
568 }
569
570 fn set_axis_pair_as_gamepad(&self, world: &mut World, value: Vec2, gamepad: Option<Entity>) {
572 let mut query_state = SystemState::<Query<Entity, With<Gamepad>>>::new(world);
573 let query = query_state.get(world);
574 let gamepad = gamepad.unwrap_or(find_gamepad(Some(query)));
575
576 let message = RawGamepadEvent::Axis(RawGamepadAxisChangedEvent {
577 gamepad,
578 axis: self.x,
579 value: value.x,
580 });
581 world
582 .resource_mut::<Messages<RawGamepadEvent>>()
583 .write(message);
584
585 let message = RawGamepadEvent::Axis(RawGamepadAxisChangedEvent {
586 gamepad,
587 axis: self.y,
588 value: value.y,
589 });
590 world
591 .resource_mut::<Messages<RawGamepadEvent>>()
592 .write(message);
593 }
594}
595
596impl WithDualAxisProcessingPipelineExt for GamepadStick {
597 #[inline]
598 fn reset_processing_pipeline(mut self) -> Self {
599 self.processors.clear();
600 self
601 }
602
603 #[inline]
604 fn replace_processing_pipeline(
605 mut self,
606 processor: impl IntoIterator<Item = DualAxisProcessor>,
607 ) -> Self {
608 self.processors = processor.into_iter().collect();
609 self
610 }
611
612 #[inline]
613 fn with_processor(mut self, processor: impl Into<DualAxisProcessor>) -> Self {
614 self.processors.push(processor.into());
615 self
616 }
617}
618
619#[must_use]
621#[inline]
622fn button_pressed(
623 input_store: &CentralInputStore,
624 gamepad: Entity,
625 button: GamepadButton,
626) -> Option<bool> {
627 let button = SpecificGamepadButton::new(gamepad, button);
628 input_store.pressed(&button)
629}
630
631#[must_use]
637#[inline]
638fn button_value(input_store: &CentralInputStore, gamepad: Entity, button: GamepadButton) -> f32 {
639 let button = SpecificGamepadButton::new(gamepad, button);
640 input_store.button_value(&button)
641}
642
643#[derive(SystemParam)]
645pub struct GamepadButtonInput<'w> {
646 pub buttons: Res<'w, ButtonInput<GamepadButton>>,
648
649 pub axes: Res<'w, Axis<GamepadButton>>,
651}
652
653impl UpdatableInput for GamepadButton {
654 type SourceData = SQuery<(Entity, Read<Gamepad>)>;
655
656 fn compute(
657 mut central_input_store: ResMut<CentralInputStore>,
658 source_data: StaticSystemParam<Self::SourceData>,
659 ) {
660 for (gamepad_entity, gamepad) in source_data.iter() {
661 for gamepad_button in GamepadButton::all() {
662 let specific_button = SpecificGamepadButton {
663 gamepad: gamepad_entity,
664 button: gamepad_button,
665 };
666 let pressed = gamepad.pressed(gamepad_button);
667 let value = gamepad.get(gamepad_button).unwrap_or(f32::from(pressed));
668 let button_value = ButtonValue::new(pressed, value);
669 central_input_store.update_buttonlike(specific_button, button_value);
670 central_input_store.update_buttonlike(gamepad_button, button_value);
671 }
672 }
673 }
674}
675
676impl UserInput for SpecificGamepadButton {
680 fn kind(&self) -> InputControlKind {
681 InputControlKind::Button
682 }
683
684 fn decompose(&self) -> BasicInputs {
685 BasicInputs::Simple(Box::new(*self))
686 }
687}
688
689#[serde_typetag]
690impl Buttonlike for SpecificGamepadButton {
691 fn get_pressed(&self, input_store: &CentralInputStore, _gamepad: Entity) -> Option<bool> {
693 button_pressed(input_store, self.gamepad, self.button)
694 }
695
696 fn value(&self, input_store: &CentralInputStore, _gamepad: Entity) -> f32 {
698 button_value(input_store, self.gamepad, self.button)
699 }
700
701 fn press(&self, world: &mut World) {
702 self.set_value(world, 1.0);
703 }
704
705 fn release(&self, world: &mut World) {
706 self.set_value(world, 0.0);
707 }
708
709 fn set_value(&self, world: &mut World, value: f32) {
710 let message = RawGamepadEvent::Button(RawGamepadButtonChangedEvent {
711 gamepad: self.gamepad,
712 button: self.button,
713 value,
714 });
715 world
716 .resource_mut::<Messages<RawGamepadEvent>>()
717 .write(message);
718 }
719}
720
721impl UserInput for GamepadButton {
723 #[inline]
725 fn kind(&self) -> InputControlKind {
726 InputControlKind::Button
727 }
728
729 #[inline]
732 fn decompose(&self) -> BasicInputs {
733 BasicInputs::Simple(Box::new(*self))
734 }
735}
736
737#[serde_typetag]
738impl Buttonlike for GamepadButton {
739 #[inline]
741 fn get_pressed(&self, input_store: &CentralInputStore, gamepad: Entity) -> Option<bool> {
742 button_pressed(input_store, gamepad, *self)
743 }
744
745 #[inline]
751 fn value(&self, input_store: &CentralInputStore, gamepad: Entity) -> f32 {
752 button_value(input_store, gamepad, *self)
753 }
754
755 fn press_as_gamepad(&self, world: &mut World, gamepad: Option<Entity>) {
757 self.set_value_as_gamepad(world, 1.0, gamepad);
758 }
759
760 fn release_as_gamepad(&self, world: &mut World, gamepad: Option<Entity>) {
762 self.set_value_as_gamepad(world, 0.0, gamepad);
763 }
764
765 #[inline]
767 fn set_value_as_gamepad(&self, world: &mut World, value: f32, gamepad: Option<Entity>) {
768 let mut query_state = SystemState::<Query<Entity, With<Gamepad>>>::new(world);
769 let query = query_state.get(world);
770 let gamepad = gamepad.unwrap_or(find_gamepad(Some(query)));
771
772 let message = RawGamepadEvent::Button(RawGamepadButtonChangedEvent {
773 gamepad,
774 button: *self,
775 value,
776 });
777 world
778 .resource_mut::<Messages<RawGamepadEvent>>()
779 .write(message);
780 }
781}
782
783#[cfg(test)]
784mod tests {
785 use super::*;
786 use crate::plugin::CentralInputStorePlugin;
787 use bevy::input::gamepad::{GamepadConnection, GamepadConnectionEvent};
788 use bevy::input::InputPlugin;
789 use bevy::prelude::*;
790
791 struct TestContext {
792 pub app: App,
793 }
794
795 impl TestContext {
796 pub fn new() -> Self {
797 let mut app = App::new();
798 app.add_plugins(MinimalPlugins);
799 app.add_plugins((InputPlugin, CentralInputStorePlugin));
800 Self { app }
801 }
802
803 pub fn send_gamepad_connection_event(&mut self, gamepad: Option<Entity>) -> Entity {
804 let gamepad = gamepad.unwrap_or_else(|| self.app.world_mut().spawn_empty().id());
805 self.app
806 .world_mut()
807 .resource_mut::<Messages<GamepadConnectionEvent>>()
808 .write(GamepadConnectionEvent::new(
809 gamepad,
810 GamepadConnection::Connected {
811 name: "TestController".to_string(),
812 vendor_id: None,
813 product_id: None,
814 },
815 ));
816 gamepad
817 }
818
819 pub fn update(&mut self) {
820 self.app.update();
821 }
822
823 pub fn send_raw_gamepad_event(&mut self, event: RawGamepadEvent) {
824 self.app
825 .world_mut()
826 .resource_mut::<Messages<RawGamepadEvent>>()
827 .write(event);
828 }
829 }
830
831 #[test]
832 fn test_gamepad_axes() {
833 let left_up = GamepadControlDirection::LEFT_UP;
834 assert_eq!(left_up.kind(), InputControlKind::Button);
835
836 let left_down = GamepadControlDirection::LEFT_DOWN;
838 assert_eq!(left_down.kind(), InputControlKind::Button);
839
840 let left_x = GamepadControlAxis::LEFT_X;
841 assert_eq!(left_x.kind(), InputControlKind::Axis);
842
843 let left_y = GamepadControlAxis::LEFT_Y;
844 assert_eq!(left_y.kind(), InputControlKind::Axis);
845
846 let left = GamepadStick::LEFT;
847 assert_eq!(left.kind(), InputControlKind::DualAxis);
848
849 let right_up = GamepadControlDirection::RIGHT_DOWN;
851 assert_eq!(right_up.kind(), InputControlKind::Button);
852
853 let right_y = GamepadControlAxis::RIGHT_Y;
854 assert_eq!(right_y.kind(), InputControlKind::Axis);
855
856 let right = GamepadStick::RIGHT;
857 assert_eq!(right.kind(), InputControlKind::DualAxis);
858
859 let mut ctx = TestContext::new();
861 ctx.update();
862
863 let gamepad = ctx.send_gamepad_connection_event(None);
864 ctx.update();
865
866 let inputs = ctx.app.world().resource::<CentralInputStore>();
867 assert!(!left_up.pressed(inputs, gamepad));
868 assert!(!left_down.pressed(inputs, gamepad));
869 assert!(!right_up.pressed(inputs, gamepad));
870 assert_eq!(left_x.value(inputs, gamepad), 0.0);
871 assert_eq!(left_y.value(inputs, gamepad), 0.0);
872 assert_eq!(right_y.value(inputs, gamepad), 0.0);
873 assert_eq!(left.axis_pair(inputs, gamepad), Vec2::ZERO);
874 assert_eq!(right.axis_pair(inputs, gamepad), Vec2::ZERO);
875
876 let mut ctx = TestContext::new();
877 ctx.update();
878
879 let gamepad = ctx.send_gamepad_connection_event(None);
880 ctx.send_raw_gamepad_event(RawGamepadEvent::Axis(RawGamepadAxisChangedEvent {
881 gamepad,
882 axis: GamepadControlDirection::LEFT_UP.axis,
883 value: GamepadControlDirection::LEFT_UP
884 .direction
885 .full_active_value(),
886 }));
887 ctx.update();
888
889 let inputs: &CentralInputStore = ctx.app.world().resource::<CentralInputStore>();
890 assert!(left_up.pressed(inputs, gamepad));
891 assert!(!left_down.pressed(inputs, gamepad));
892 assert!(!right_up.pressed(inputs, gamepad));
893 assert_eq!(left_x.value(inputs, gamepad), 0.0);
894 assert_eq!(left_y.value(inputs, gamepad), 1.0);
895 assert_eq!(right_y.value(inputs, gamepad), 0.0);
896 assert_eq!(left.axis_pair(inputs, gamepad), Vec2::new(0.0, 1.0));
897 assert_eq!(right.axis_pair(inputs, gamepad), Vec2::ZERO);
898
899 let mut ctx = TestContext::new();
900 ctx.update();
901
902 let gamepad = ctx.send_gamepad_connection_event(None);
903 ctx.send_raw_gamepad_event(RawGamepadEvent::Axis(RawGamepadAxisChangedEvent {
904 gamepad,
905 axis: GamepadControlDirection::LEFT_UP.axis,
906 value: 0.6,
907 }));
908 ctx.update();
909
910 let inputs: &CentralInputStore = ctx.app.world().resource::<CentralInputStore>();
911 assert!(left_up.pressed(inputs, gamepad));
912 assert!(!left_down.pressed(inputs, gamepad));
913 assert!(!right_up.pressed(inputs, gamepad));
914 assert_eq!(left_x.value(inputs, gamepad), 0.0);
915 assert_eq!(left_y.value(inputs, gamepad), 0.6);
916 assert_eq!(right_y.value(inputs, gamepad), 0.0);
917 assert_eq!(left.axis_pair(inputs, gamepad), Vec2::new(0.0, 0.6));
918 assert_eq!(right.axis_pair(inputs, gamepad), Vec2::ZERO);
919
920 let mut ctx = TestContext::new();
921 ctx.update();
922
923 let gamepad = ctx.send_gamepad_connection_event(None);
924 ctx.send_raw_gamepad_event(RawGamepadEvent::Axis(RawGamepadAxisChangedEvent {
925 gamepad,
926 axis: GamepadStick::LEFT.x,
927 value: 0.6,
928 }));
929 ctx.send_raw_gamepad_event(RawGamepadEvent::Axis(RawGamepadAxisChangedEvent {
930 gamepad,
931 axis: GamepadAxis::LeftStickY,
932 value: 0.4,
933 }));
934 ctx.update();
935
936 let inputs: &CentralInputStore = ctx.app.world().resource::<CentralInputStore>();
937 assert!(left_up.pressed(inputs, gamepad));
938 assert!(!left_down.pressed(inputs, gamepad));
939 assert!(!right_up.pressed(inputs, gamepad));
940 assert_eq!(left_x.value(inputs, gamepad), 0.6);
941 assert_eq!(left_y.value(inputs, gamepad), 0.4);
942 assert_eq!(right_y.value(inputs, gamepad), 0.0);
943 assert_eq!(left.axis_pair(inputs, gamepad), Vec2::new(0.6, 0.4));
944 assert_eq!(right.axis_pair(inputs, gamepad), Vec2::ZERO);
945 }
946
947 #[test]
948 fn test_gamepad_buttons() {
949 let up = GamepadButton::DPadUp;
950 assert_eq!(up.kind(), InputControlKind::Button);
951
952 let left = GamepadButton::DPadLeft;
953 assert_eq!(left.kind(), InputControlKind::Button);
954
955 let down = GamepadButton::DPadDown;
956 assert_eq!(left.kind(), InputControlKind::Button);
957
958 let right = GamepadButton::DPadRight;
959 assert_eq!(left.kind(), InputControlKind::Button);
960
961 let mut ctx = TestContext::new();
963 ctx.update();
964
965 let gamepad = ctx.send_gamepad_connection_event(None);
966 ctx.update();
967
968 let inputs = ctx.app.world().resource::<CentralInputStore>();
969 assert!(!up.pressed(inputs, gamepad));
970 assert!(!left.pressed(inputs, gamepad));
971 assert!(!down.pressed(inputs, gamepad));
972 assert!(!right.pressed(inputs, gamepad));
973
974 let mut ctx = TestContext::new();
976 ctx.update();
977
978 let gamepad = ctx.send_gamepad_connection_event(None);
979 ctx.send_raw_gamepad_event(RawGamepadEvent::Button(RawGamepadButtonChangedEvent {
980 gamepad,
981 button: GamepadButton::DPadLeft,
982 value: 1.0,
983 }));
984 ctx.update();
985
986 let inputs = ctx.app.world().resource::<CentralInputStore>();
987 assert!(!up.pressed(inputs, gamepad));
988 assert!(left.pressed(inputs, gamepad));
989 assert!(!down.pressed(inputs, gamepad));
990 assert!(!right.pressed(inputs, gamepad));
991 }
992
993 #[test]
994 fn test_gamepad_button_values() {
995 let up = GamepadButton::DPadUp;
996 assert_eq!(up.kind(), InputControlKind::Button);
997
998 let left = GamepadButton::DPadLeft;
999 assert_eq!(left.kind(), InputControlKind::Button);
1000
1001 let down = GamepadButton::DPadDown;
1002 assert_eq!(down.kind(), InputControlKind::Button);
1003
1004 let right = GamepadButton::DPadRight;
1005 assert_eq!(right.kind(), InputControlKind::Button);
1006
1007 let mut ctx = TestContext::new();
1009 ctx.update();
1010
1011 let gamepad = ctx.send_gamepad_connection_event(None);
1012 ctx.update();
1013
1014 let inputs = ctx.app.world().resource::<CentralInputStore>();
1015 assert_eq!(up.value(inputs, gamepad), 0.0);
1016 assert_eq!(left.value(inputs, gamepad), 0.0);
1017 assert_eq!(down.value(inputs, gamepad), 0.0);
1018 assert_eq!(right.value(inputs, gamepad), 0.0);
1019
1020 let mut ctx = TestContext::new();
1022 ctx.update();
1023
1024 let gamepad = ctx.send_gamepad_connection_event(None);
1025 ctx.send_raw_gamepad_event(RawGamepadEvent::Button(RawGamepadButtonChangedEvent {
1026 gamepad,
1027 button: GamepadButton::DPadLeft,
1028 value: 1.0,
1029 }));
1030 ctx.update();
1031
1032 let inputs = ctx.app.world().resource::<CentralInputStore>();
1033 assert_eq!(up.value(inputs, gamepad), 0.0);
1034 assert_eq!(left.value(inputs, gamepad), 1.0);
1035 assert_eq!(down.value(inputs, gamepad), 0.0);
1036 assert_eq!(right.value(inputs, gamepad), 0.0);
1037 }
1038
1039 #[test]
1040 fn test_gamepad_triggers() {
1041 let trigger = GamepadButton::RightTrigger2;
1042 assert_eq!(trigger.kind(), InputControlKind::Button);
1043
1044 let mut ctx = TestContext::new();
1046 ctx.update();
1047
1048 let gamepad = ctx.send_gamepad_connection_event(None);
1049 ctx.update();
1050
1051 let inputs: &CentralInputStore = ctx.app.world().resource::<CentralInputStore>();
1052 assert_eq!(trigger.value(inputs, gamepad), 0.0);
1053
1054 let mut ctx = TestContext::new();
1056 ctx.update();
1057
1058 let gamepad = ctx.send_gamepad_connection_event(None);
1059 ctx.send_raw_gamepad_event(RawGamepadEvent::Button(RawGamepadButtonChangedEvent {
1060 gamepad,
1061
1062 button: trigger,
1063 value: 0.8,
1064 }));
1065 ctx.update();
1066
1067 let inputs: &CentralInputStore = ctx.app.world().resource::<CentralInputStore>();
1068 assert!(trigger.pressed(inputs, gamepad));
1069 assert_eq!(trigger.value(inputs, gamepad), 0.8);
1070
1071 let mut ctx = TestContext::new();
1073 ctx.update();
1074
1075 let gamepad = ctx.send_gamepad_connection_event(None);
1076 ctx.send_raw_gamepad_event(RawGamepadEvent::Button(RawGamepadButtonChangedEvent {
1077 gamepad,
1078 button: trigger,
1079 value: 0.4,
1080 }));
1081 ctx.update();
1082
1083 let inputs: &CentralInputStore = ctx.app.world().resource::<CentralInputStore>();
1084 assert!(trigger.released(inputs, gamepad));
1085 assert_eq!(trigger.value(inputs, gamepad), 0.4);
1086 }
1087}