Skip to main content

GamepadState.ixx File

Representation of current gamepad input state. More...

Included Headers

#include <algorithm> #include <cassert> #include <helios/helios_config.h> #include <helios.input.types.Gamepad> #include <helios.util.log.LogManager> #include <helios.util.log.Logger> #include <helios.math.types>

Namespaces Index

namespacehelios
namespaceinput

Input handling and management. More...

namespacegamepad

Gamepad input handling and configuration. More...

Classes Index

classGamepadState

A lightweight class for transferring the state of a Gamepad. More...

Macro Definitions Index

#defineHELIOS_LOG_SCOPE   "helios::input::gamepad::GamepadState"

Description

Representation of current gamepad input state.

Macro Definitions

HELIOS_LOG_SCOPE

#define HELIOS_LOG_SCOPE   "helios::input::gamepad::GamepadState"

Definition at line 20 of file GamepadState.ixx.

20#define HELIOS_LOG_SCOPE "helios::input::gamepad::GamepadState"

File Listing

The file content with the documentation metadata removed is:

1/**
2 * @file GamepadState.ixx
3 * @brief Representation of current gamepad input state.
4 */
5module;
6
7#include <algorithm>
8#include <cassert>
9#include <helios/helios_config.h>
10
11export module helios.input.gamepad.GamepadState;
12
13import helios.math.types;
14import helios.util.log.Logger;
15import helios.util.log.LogManager;
16
17import helios.input.types.Gamepad;
18
19
20#define HELIOS_LOG_SCOPE "helios::input::gamepad::GamepadState"
21
22using namespace helios::input::types;
23
24export namespace helios::input::gamepad {
25
26 /**
27 * @brief A lightweight class for transferring the state of a Gamepad.
28 *
29 * This class models a reusable object representing the input state of a gamepad.
30 * The input state of a gamepad consists of the state of the left and right trigger,
31 * ranging from [0, 1], where 1 means fully pressed and 0 means not pressed at all.
32 *
33 * Similarly, a GamepadState provides information about the left and the right
34 * gamepad axes. Both the x- and the y-axis range from [-1, 1]. For the x-axis,
35 * the following holds:
36 * - 0 => not moved at all
37 * - -1 => moved all the way to the left
38 * - 1 => moved all the way to the right
39 *
40 * For the y-axis the positive direction corresponds to "up" and the negative
41 * direction corresponds to "down".
42 *
43 * Updating the values is done by calling `updateAxes` on this object.
44 *
45 * For convenient access of the axes in 2D coordinates, the class provides accessors
46 * to the sticks' axes in `vec2f` form.
47 *
48 * Button states are generally represented with boolean values, i.e. true for pressed,
49 * otherwise false.
50 *
51 * @note Implementations must account for joystick drift: achieving exactly 0
52 * for x/y when no human input occurred is rare. Consider applying a dead zone
53 * when processing the GamepadState. Axis values are individually clipped to [-1.0, 1.0],
54 * but the magnitude of the resulting (x, y) vector may exceed 1.0. Applications should
55 * normalize input vectors where appropriate and possibly offer calibration.
56 *
57 * This implementation is inspired by the GLFW gamepad input model.
58 * @see https://www.glfw.org/docs/latest/input_guide.html#joystick
59 */
61 private:
62
63 uint32_t currInput_ = 0;
64 uint32_t prevInput_ = 0;
65
66 /**
67 * @brief Shared logger instance for all GamepadState objects.
68 */
70
71
72 /**
73 * @brief State of the A button (true if pressed).
74 */
75 bool buttonA_ = false;
76 bool prevButtonA_ = false;
77
78 /**
79 * @brief State of the B button (true if pressed).
80 */
81 bool buttonB_ = false;
82 bool prevButtonB_ = false;
83
84 /**
85 * @brief State of the X button (true if pressed).
86 */
87 bool buttonX_ = false;
88
89 /**
90 * @brief State of the Y button (true if pressed).
91 */
92 bool buttonY_ = false;
93
94 /**
95 * @brief State of the Start button (true if pressed).
96 */
97 bool buttonStart_ = false;
98
99 bool prevButtonStart_ = false;
100
101 /**
102 * @brief State of the Back button (true if pressed).
103 */
104 bool buttonBack_ = false;
105
106 /**
107 * @brief State of the Guide button (true if pressed).
108 */
109 bool buttonGuide_ = false;
110
111 /**
112 * @brief State of the left bumper button (true if pressed).
113 */
114 bool buttonLeftBumper_ = false;
115
116 /**
117 * @brief State of the right bumper button (true if pressed).
118 */
119 bool buttonRightBumper_ = false;
120
121 /**
122 * @brief State of the left thumbstick button (true if pressed).
123 */
124 bool buttonLeftThumb_ = false;
125
126 /**
127 * @brief State of the right thumbstick button (true if pressed).
128 */
129 bool buttonRightThumb_ = false;
130
131 /**
132 * @brief State of the D-pad up button (true if pressed).
133 */
134 bool buttonDpadUp_ = false;
135 bool prevButtonDpadUp_ = false;
136
137 /**
138 * @brief State of the D-pad right button (true if pressed).
139 */
140 bool buttonDpadRight_ = false;
141
142 /**
143 * @brief State of the D-pad down button (true if pressed).
144 */
145 bool buttonDpadDown_ = false;
146 bool prevButtonDpadDown_ = false;
147
148 /**
149 * @brief State of the D-pad left button (true if pressed).
150 */
151 bool buttonDpadLeft_ = false;
152
153 /**
154 * @brief Flag to indicate if the cached vec2f members need to be recalculated.
155 */
156 mutable bool needsUpdate_ = true;
157
158 /**
159 * @brief Raw float value of the left stick's x-axis.
160 */
161 float axisLeftX_{};
162
163 /**
164 * @brief Raw float value of the left stick's y-axis.
165 */
166 float axisLeftY_{};
167
168 /**
169 * @brief Raw float value of the right stick's x-axis.
170 */
171 float axisRightX_{};
172
173 /**
174 * @brief Raw float value of the right stick's y-axis.
175 */
176 float axisRightY_{};
177
178 /**
179 * @brief Raw float value of the left trigger.
180 */
181 float triggerLeft_{};
182
183 /**
184 * @brief Raw float value of the right trigger.
185 */
186 float triggerRight_{};
187
188 /**
189 * @brief Cached vec2f representation of the left stick (x, y).
190 */
191 mutable helios::math::vec2f left_;
192
193 /**
194 * @brief Cached vec2f representation of the right stick (x, y).
195 */
196 mutable helios::math::vec2f right_;
197
198 /**
199 * @brief Cached vec2f representation of the triggers (left, right).
200 */
201 mutable helios::math::vec2f trigger_;
202
203 /**
204 * @brief Internal method to update the cached vec2f representations.
205 * This method should be called when the current values of the axes are queried
206 * in vec2f form and `needsUpdate_` evaluates to `true`.
207 */
208 void update() const noexcept {
209 left_ = helios::math::vec2f(axisLeftX_, axisLeftY_);
210 right_ = helios::math::vec2f(axisRightX_, axisRightY_);
211 trigger_ = helios::math::vec2f(triggerLeft_, triggerRight_);
212
213 needsUpdate_ = false;
214 }
215
216 public:
217 ~GamepadState() = default;
218
219 /**
220 * @brief Default constructor.
221 *
222 * Initializes all the values with 0.0f.
223 */
224 GamepadState() = default;
225
226 /**
227 * @brief Creates a new GamepadState object.
228 *
229 * Delegates to `updateAxes` for value initialization.
230 *
231 * @see updateAxes
232 */
233 explicit GamepadState(
234 float axisLeftX, float axisLeftY, float axisRightX,
235 float axisRightY, float triggerLeft, float triggerRight,
236
237 bool buttonA, bool buttonB, bool buttonX, bool buttonY,
238 bool buttonStart, bool buttonBack, bool buttonGuide,
242
243 ) noexcept {
245 axisLeftX, axisLeftY, axisRightX, axisRightY, triggerLeft, triggerRight,
246
250 }
251
252
253 /**
254 * @brief Updates the axes and button states of this GamepadState object.
255 *
256 * This method updates raw axis and trigger values and stores the boolean
257 * state of all standard gamepad buttons. Float parameters are asserted to
258 * be in their expected ranges and then clamped.
259 *
260 * For the sticks' range [-1, 1] the following holds:
261 * -1 means moved all the way left/down, 0 means not moved at all, 1 means moved all the way right/up.
262 *
263 * For the triggers' range of [0, 1] the following holds:
264 * 0 means not pressed, 1 means fully pressed.
265 *
266 * @param axisLeftX The x-axis of the left stick, expected in [-1, 1].
267 * @param axisLeftY The y-axis of the left stick, expected in [-1, 1].
268 * @param axisRightX The x-axis of the right stick, expected in [-1, 1].
269 * @param axisRightY The y-axis of the right stick, expected in [-1, 1].
270 * @param triggerLeft The left trigger value, expected in [0, 1].
271 * @param triggerRight The right trigger value, expected in [0, 1].
272 * @param buttonA True if the A button is pressed.
273 * @param buttonB True if the B button is pressed.
274 * @param buttonX True if the X button is pressed.
275 * @param buttonY True if the Y button is pressed.
276 * @param buttonStart True if the Start button is pressed.
277 * @param buttonBack True if the Back button is pressed.
278 * @param buttonGuide True if the Guide (platform) button is pressed.
279 * @param buttonLeftBumper True if the left bumper is pressed.
280 * @param buttonRightBumper True if the right bumper is pressed.
281 * @param buttonLeftThumb True if the left thumbstick button is pressed.
282 * @param buttonRightThumb True if the right thumbstick button is pressed.
283 * @param buttonDpadUp True if the D-pad Up button is pressed.
284 * @param buttonDpadRight True if the D-pad Right button is pressed.
285 * @param buttonDpadDown True if the D-pad Down button is pressed.
286 * @param buttonDpadLeft True if the D-pad Left button is pressed.
287 */
289 float axisLeftX, float axisLeftY, float axisRightX, float axisRightY,
290 float triggerLeft, float triggerRight,
291
292 bool buttonA, bool buttonB, bool buttonX, bool buttonY,
293 bool buttonStart, bool buttonBack, bool buttonGuide,
297 ) noexcept {
298
299#ifdef HELIOS_DEBUG
300 if(axisLeftX < -1.0f || axisLeftX > 1.0f) {
301 logger_.warn("axisLeftX is out of bounds.");
302 }
303 if(axisLeftY < -1.0f || axisLeftY > 1.0f) {
304 logger_.warn("axisLeftY is out of bounds.");
305 }
306 if(axisRightX < -1.0f || axisRightX > 1.0f) {
307 logger_.warn("axisRightX is out of bounds.");
308 }
309 if(axisRightY < -1.0f || axisRightY > 1.0f) {
310 logger_.warn("axisRightY is out of bounds.");
311 }
313 logger_.warn("triggerLeft is out of bounds.");
314 }
316 logger_.warn("triggerRight is out of bounds.");
317 }
318#endif
319
320 axisLeftX_ = std::clamp(axisLeftX, -1.0f, 1.0f);
321 axisLeftY_ = std::clamp(axisLeftY, -1.0f, 1.0f);
322 axisRightX_ = std::clamp(axisRightX, -1.0f, 1.0f);
323 axisRightY_ = std::clamp(axisRightY, -1.0f, 1.0f);
324 triggerLeft_ = std::clamp(triggerLeft, 0.0f, 1.0f);
325 triggerRight_ = std::clamp(triggerRight, 0.0f, 1.0f);
326;
327
328 prevButtonA_ = buttonA_;
329 buttonA_ = buttonA;
330
331 prevButtonB_ = buttonB_;
332 buttonB_ = buttonB;
333
334 buttonX_ = buttonX;
335
336 buttonY_ = buttonY;
337
338 prevButtonStart_ = buttonStart_;
339 buttonStart_ = buttonStart;
340
341 buttonBack_ = buttonBack;
342
343 buttonGuide_ = buttonGuide;
344
345 buttonLeftBumper_ = buttonLeftBumper;
346
347 buttonRightBumper_ = buttonRightBumper;
348
349 buttonLeftThumb_ = buttonLeftThumb;
350
351 buttonRightThumb_ = buttonRightThumb;
352
353 prevButtonDpadUp_ = buttonDpadUp_;
354 buttonDpadUp_ = buttonDpadUp;
355
356 buttonDpadRight_ = buttonDpadRight;
357
358 prevButtonDpadDown_ = buttonDpadDown_;
359 buttonDpadDown_ = buttonDpadDown;
360
361 buttonDpadLeft_ = buttonDpadLeft;
362
363 needsUpdate_ = true;
364 }
365
366 /**
367 * @brief Returns the current value of the left stick's x-axis.
368 *
369 * @return float value in the range [-1, 1]
370 */
371 [[nodiscard]] float leftX() const noexcept {
372 return axisLeftX_;
373 }
374
375
376 /**
377 * @brief Returns the current value of the left stick's y-axis.
378 *
379 * @return float value in the range [-1, 1]
380 */
381 [[nodiscard]] float leftY() const noexcept {
382 return axisLeftY_;
383 }
384
385
386 /**
387 * @brief Returns the current value of the right stick's x-axis.
388 *
389 * @return float value in the range [-1, 1]
390 */
391 [[nodiscard]] float rightX() const noexcept {
392 return axisRightX_;
393 }
394
395
396 /**
397 * @brief Returns the current value of the right stick's y-axis.
398 *
399 * @return float value in the range [-1, 1]
400 */
401 [[nodiscard]] float rightY() const noexcept {
402 return axisRightY_;
403 }
404
405
406 /**
407 * @brief Returns the current value of the left trigger.
408 *
409 * @return float value in the range [0, 1]
410 */
411 [[nodiscard]] float triggerLeft() const noexcept {
412 return triggerLeft_;
413 }
414
415
416 /**
417 * @brief Returns the current value of the right trigger.
418 *
419 * @return float value in the range [0, 1]
420 */
421 [[nodiscard]] float triggerRight() const noexcept {
422 return triggerRight_;
423 }
424
425
426 /**
427 * @brief Returns the state of the left stick as a helios::math::vec2f.
428 *
429 * @return A helios::math::vec2f, with the first component being the x-axis, the second component the y-axis.
430 */
431 [[nodiscard]] helios::math::vec2f left() const noexcept {
432 if (needsUpdate_) {
433 update();
434 }
435 return left_;
436 }
437
438
439 /**
440 * @brief Returns the state of the right stick as a helios::math::vec2f.
441 *
442 * @return A helios::math::vec2f, with the first component being the x-axis, the second component the y-axis.
443 */
444 [[nodiscard]] helios::math::vec2f right() const noexcept {
445 if (needsUpdate_) {
446 update();
447 }
448 return right_;
449 }
450
451
452 /**
453 * @brief Returns the state of the triggers as a helios::math::vec2f.
454 *
455 * @return A helios::math::vec2f, with the first component being the left trigger-axis,
456 * the second component the right trigger-axis.
457 */
458 [[nodiscard]] helios::math::vec2f trigger() const noexcept {
459 if (needsUpdate_) {
460 update();
461 }
462 return trigger_;
463 }
464
465
466 /**
467 * @brief Returns true when the A button is pressed.
468 * @return true if pressed, false otherwise.
469 */
470 [[nodiscard]] bool buttonA() const noexcept {
471 return buttonA_;
472 };
473
474
475 /**
476 * @brief Returns true when the B button is pressed.
477 * @return true if pressed, false otherwise.
478 */
479 [[nodiscard]] bool buttonB() const noexcept {
480 return buttonB_;
481 };
482
483
484 /**
485 * @brief Returns true when the X button is pressed.
486 * @return true if pressed, false otherwise.
487 */
488 [[nodiscard]] bool buttonX() const noexcept {
489 return buttonX_;
490 };
491
492
493 /**
494 * @brief Returns true when the Y button is pressed.
495 * @return true if pressed, false otherwise.
496 */
497 [[nodiscard]] bool buttonY() const noexcept {
498 return buttonY_;
499 };
500
501
502 /**
503 * @brief Checks if a button is currently held down.
504 *
505 * @param input The button to check.
506 *
507 * @return True if the button is currently pressed.
508 */
509 [[nodiscard]] bool isButtonDown(const GamepadInput input) const {
510 switch (input) {
512 return buttonStart_;
514 return buttonDpadDown_;
516 return buttonDpadUp_;
517 case GamepadInput::A:
518 return buttonA_;
519 case GamepadInput::B:
520 return buttonB_;
521
522 default:
523 assert(false && "Unexpected input");
524 return false;
525 }
526 }
527
528 /**
529 * @brief Checks if a button has been held for at least 2 frames.
530 *
531 * @param input The button to check.
532 *
533 * @return True if the button was pressed last frame and is still pressed.
534 */
535 [[nodiscard]] bool isButtonHeld(const GamepadInput input) const {
536 switch (input) {
538 return prevButtonStart_ && buttonStart_;
540 return prevButtonDpadDown_ && buttonDpadDown_;
542 return prevButtonDpadUp_ && buttonDpadUp_;
543 case GamepadInput::A:
544 return prevButtonA_ && buttonA_;
545 case GamepadInput::B:
546 return prevButtonB_ && buttonB_;
547
548 default:
549 assert(false && "Unexpected input");
550 return false;
551 }
552 }
553
554 /**
555 * @brief Checks if a button was released this frame.
556 *
557 * @param input The button to check.
558 *
559 * @return True if the button was pressed last frame but is now released.
560 */
561 [[nodiscard]] bool isButtonUp(const GamepadInput input) const {
562 switch (input) {
564 return prevButtonStart_ && !buttonStart_;
566 return prevButtonDpadDown_ && !buttonDpadDown_;
568 return prevButtonDpadUp_ && !buttonDpadUp_;
569 case GamepadInput::A:
570 return prevButtonA_ && !buttonA_;
571 case GamepadInput::B:
572 return prevButtonB_ && !buttonB_;
573
574 default:
575 assert(false && "Unexpected input");
576 return false;
577 }
578 }
579
580
581 /**
582 * @brief Checks if a button was pressed this frame.
583 *
584 * @param input The button to check.
585 *
586 * @return True if the button was not pressed last frame but is pressed now.
587 */
588 [[nodiscard]] bool isButtonPressed(const GamepadInput input) const {
589 switch (input) {
591 return !prevButtonStart_ && buttonStart_;
593 return !prevButtonDpadDown_ && buttonDpadDown_;
595 return !prevButtonDpadUp_ && buttonDpadUp_;
596 case GamepadInput::A:
597 return !prevButtonA_ && buttonA_;
598 case GamepadInput::B:
599 return !prevButtonB_ && buttonB_;
600
601 default:
602 assert(false && "Unexpected input");
603 return false;
604 }
605 }
606
607
608
609 /**
610 * @brief Returns true when the Start button is pressed.
611 * @return true if pressed, false otherwise.
612 */
613 [[nodiscard]] bool buttonStart() const noexcept {
614 return prevButtonStart_ && buttonStart_;
615 };
616
617 /**
618 * @brief Returns true if Start was pressed this frame.
619 *
620 * @return True if Start was just pressed.
621 */
622 [[nodiscard]] bool buttonPressedStart() const noexcept {
623 return !prevButtonStart_ && buttonStart_;
624 };
625
626
627 /**
628 * @brief Returns true when the Back button is pressed.
629 * @return true if pressed, false otherwise.
630 */
631 [[nodiscard]] bool buttonBack() const noexcept {
632 return buttonBack_;
633 };
634
635
636 /**
637 * @brief Returns true when the Guide button is pressed.
638 * @return true if pressed, false otherwise.
639 */
640 [[nodiscard]] bool buttonGuide() const noexcept {
641 return buttonGuide_;
642 };
643
644
645 /**
646 * @brief Returns true when the left bumper is pressed.
647 * @return true if pressed, false otherwise.
648 */
649 [[nodiscard]] bool buttonLeftBumper() const noexcept {
650 return buttonLeftBumper_;
651 };
652
653
654 /**
655 * @brief Returns true when the right bumper is pressed.
656 * @return true if pressed, false otherwise.
657 */
658 [[nodiscard]] bool buttonRightBumper() const noexcept {
659 return buttonRightBumper_;
660 };
661
662
663 /**
664 * @brief Returns true when the left thumbstick button is pressed.
665 * @return true if pressed, false otherwise.
666 */
667 [[nodiscard]] bool buttonLeftThumb() const noexcept {
668 return buttonLeftThumb_;
669 };
670
671
672 /**
673 * @brief Returns true when the right thumbstick button is pressed.
674 * @return true if pressed, false otherwise.
675 */
676 [[nodiscard]] bool buttonRightThumb() const noexcept {
677 return buttonRightThumb_;
678 };
679
680
681 /**
682 * @brief Returns true when the D-pad Up button is pressed.
683 * @return true if pressed, false otherwise.
684 */
685 [[nodiscard]] bool buttonDpadUp() const noexcept {
686 return buttonDpadUp_;
687 };
688
689
690 /**
691 * @brief Returns true when the D-pad Right button is pressed.
692 * @return true if pressed, false otherwise.
693 */
694 [[nodiscard]] bool buttonDpadRight() const noexcept {
695 return buttonDpadRight_;
696 };
697
698
699 /**
700 * @brief Returns true when the D-pad Down button is pressed.
701 * @return true if pressed, false otherwise.
702 */
703 [[nodiscard]] bool buttonDpadDown() const noexcept {
704 return buttonDpadDown_;
705 };
706
707
708 /**
709 * @brief Returns true when the D-pad Left button is pressed.
710 * @return true if pressed, false otherwise.
711 */
712 [[nodiscard]] bool buttonDpadLeft() const noexcept {
713 return buttonDpadLeft_;
714 };
715 };
716
717
718} // namespace helios::input

Generated via doxygen2docusaurus 2.0.0 by Doxygen 1.15.0.