Add a bunch of docs for input

This commit is contained in:
Gwilym Kuiper 2022-10-13 21:20:59 +01:00
parent e666ebbd05
commit dfa70f4335

View file

@ -2,10 +2,30 @@ use crate::fixnum::Vector2D;
use bitflags::bitflags; use bitflags::bitflags;
use core::convert::From; use core::convert::From;
/// Tri-state enum. Allows for -1, 0 and +1.
/// Useful if checking if the D-Pad is pointing left, right, or unpressed.
///
/// Note that [Tri] can be converted directly to a signed integer, so can easily be used to update positions of things in games
///
/// # Examples
/// ```rust,no_run
/// # #![no_std]
/// use agb::input::Tri;
///
/// # fn main() {
/// let x = 5;
/// let tri = Tri::Positive; // e.g. from button_controller.x_tri()
///
/// assert_eq!(x + tri as i32, 6);
/// # }
/// ```
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub enum Tri { pub enum Tri {
/// Right or down
Positive = 1, Positive = 1,
/// Unpressed
Zero = 0, Zero = 0,
/// Left or up
Negative = -1, Negative = -1,
} }
@ -18,16 +38,27 @@ impl From<(bool, bool)> for Tri {
} }
bitflags! { bitflags! {
/// Represents a button on the GBA
pub struct Button: u32 { pub struct Button: u32 {
/// The A button
const A = 1 << 0; const A = 1 << 0;
/// The B button
const B = 1 << 1; const B = 1 << 1;
/// The SELECT button
const SELECT = 1 << 2; const SELECT = 1 << 2;
/// The START button
const START = 1 << 3; const START = 1 << 3;
/// The RIGHT button on the D-Pad
const RIGHT = 1 << 4; const RIGHT = 1 << 4;
/// The LFET button on the D-Pad
const LEFT = 1 << 5; const LEFT = 1 << 5;
/// The UP button on the D-Pad
const UP = 1 << 6; const UP = 1 << 6;
/// The DOWN button on the D-Pad
const DOWN = 1 << 7; const DOWN = 1 << 7;
/// The R button on the D-Pad
const R = 1 << 8; const R = 1 << 8;
/// The L button on the D-Pad
const L = 1 << 9; const L = 1 << 9;
} }
} }
@ -36,6 +67,28 @@ const BUTTON_INPUT: *mut u16 = (0x04000130) as *mut u16;
// const BUTTON_INTURRUPT: *mut u16 = (0x04000132) as *mut u16; // const BUTTON_INTURRUPT: *mut u16 = (0x04000132) as *mut u16;
/// Helper to make it easy to get the current state of the GBA's buttons.
///
/// # Example
///
/// ```rust,no_run
/// # #![no_std]
/// use agb::input::{ButtonController, Tri};
///
/// # fn main() {
/// let mut input = ButtonController::new();
///
/// loop {
/// input.update(); // call update every loop
///
/// match input.x_tri() {
/// Tri::Negative => { /* left is being pressed */ }
/// Tri::Positive => { /* right is being pressed */ }
/// Tri::Zero => { /* Neither left nor right (or both) are pressed */ }
/// }
/// }
/// # }
/// ```
pub struct ButtonController { pub struct ButtonController {
previous: u16, previous: u16,
current: u16, current: u16,
@ -48,6 +101,8 @@ impl Default for ButtonController {
} }
impl ButtonController { impl ButtonController {
/// Create a new ButtonController.
/// This is the preferred way to create it.
#[must_use] #[must_use]
pub fn new() -> Self { pub fn new() -> Self {
let pressed = !unsafe { BUTTON_INPUT.read_volatile() }; let pressed = !unsafe { BUTTON_INPUT.read_volatile() };
@ -57,11 +112,16 @@ impl ButtonController {
} }
} }
/// Updates the state of the button controller.
/// You should call this every frame (either at the start or the end) to ensure that you have the latest state of each button press.
/// Calls to any method won't change until you call this.
pub fn update(&mut self) { pub fn update(&mut self) {
self.previous = self.current; self.previous = self.current;
self.current = !unsafe { BUTTON_INPUT.read_volatile() }; self.current = !unsafe { BUTTON_INPUT.read_volatile() };
} }
/// Returns [Tri::Positive] if right is pressed, [Tri::Negative] if left is pressed and [Tri::Zero] if neither or both are pressed.
/// This is the normal behaviour you'll want if you're using orthogonal inputs.
#[must_use] #[must_use]
pub fn x_tri(&self) -> Tri { pub fn x_tri(&self) -> Tri {
let left = self.is_pressed(Button::LEFT); let left = self.is_pressed(Button::LEFT);
@ -70,6 +130,8 @@ impl ButtonController {
(left, right).into() (left, right).into()
} }
/// Returns [Tri::Positive] if down is pressed, [Tri::Negative] if up is pressed and [Tri::Zero] if neither or both are pressed.
/// This is the normal behaviour you'll want if you're using orthogonal inputs.
#[must_use] #[must_use]
pub fn y_tri(&self) -> Tri { pub fn y_tri(&self) -> Tri {
let up = self.is_pressed(Button::UP); let up = self.is_pressed(Button::UP);
@ -78,6 +140,7 @@ impl ButtonController {
(up, down).into() (up, down).into()
} }
/// Returns true if all the buttons specified in `keys` are pressed.
#[must_use] #[must_use]
pub fn vector<T>(&self) -> Vector2D<T> pub fn vector<T>(&self) -> Vector2D<T>
where where
@ -121,11 +184,32 @@ impl ButtonController {
(currently_pressed & keys) != 0 (currently_pressed & keys) != 0
} }
/// Returns true if all the buttons specified in `keys` are not pressed. Equivalent to `!is_pressed(keys)`.
#[must_use] #[must_use]
pub fn is_released(&self, keys: Button) -> bool { pub fn is_released(&self, keys: Button) -> bool {
!self.is_pressed(keys) !self.is_pressed(keys)
} }
/// Returns true if all the buttons specified in `keys` went from not pressed to pressed in the last frame.
/// Very useful for menu navigation or selection if you want the players actions to only happen for one frame.
///
/// # Example
/// ```no_run,rust
/// # #![no_std]
/// use agb::input::{Button, ButtonController};
///
/// # fn main() {
/// let mut button_controller = ButtonController::new();
///
/// loop {
/// button_controller.update();
///
/// if button_controller.is_just_pressed(Button::A) {
/// // A button was just pressed, maybe select the currently selected item
/// }
/// }
/// # }
/// ```
#[must_use] #[must_use]
pub fn is_just_pressed(&self, keys: Button) -> bool { pub fn is_just_pressed(&self, keys: Button) -> bool {
let current = u32::from(self.current); let current = u32::from(self.current);
@ -134,6 +218,8 @@ impl ButtonController {
((current & keys) != 0) && ((previous & keys) == 0) ((current & keys) != 0) && ((previous & keys) == 0)
} }
/// Returns true if all the buttons specified in `keys` went from pressed to not pressed in the last frame.
/// Very useful for menu navigation or selection if you want players actions to only happen for one frame.
#[must_use] #[must_use]
pub fn is_just_released(&self, keys: Button) -> bool { pub fn is_just_released(&self, keys: Button) -> bool {
let current = u32::from(self.current); let current = u32::from(self.current);