Skip to main content

Handling User Input

Gamepad#

The main input device is the gamepad, consisting of 4 directions and 2 action buttons. On computers, players use the arrow keys and the X and Z keys. On mobile, a virtual gamepad overlay is displayed that players can tap.

The gamepad state is stored by WASM-4 as one byte in memory, with each button stored as a single bit. For example, the right directional button is stored in bit 5. We can mask the gamepad with the BUTTON_RIGHT constant to check if the right button is pressed.

let gamepad = unsafe { *GAMEPAD1 };
if gamepad & BUTTON_RIGHT != 0 {    trace("Right button is down!");}
Gamepad BitButton
0BUTTON_1 (1)
1BUTTON_2 (2)
2Unused
3Unused
4BUTTON_LEFT (16)
5BUTTON_RIGHT (32)
6BUTTON_UP (64)
7BUTTON_DOWN (128)

Checking if a button was pressed this frame#

GAMEPAD1 stores the current state of the gamepad. It's common to want to know if a button was just pressed this frame. Another way of putting it: if a button was not down last frame but is down this frame.

This can be handled by storing the previous gamepad state, and then bitwise XORing (^) it with the current gamepad state. This leaves us with a byte with only the buttons that were pressed this frame.

static mut PREVIOUS_GAMEPAD: u8 = 0;
#[no_mangle]fn update() {    let (pressed_this_frame, ..) = unsafe {        let previous = PREVIOUS_GAMEPAD;        let gamepad = *GAMEPAD1;        // Only the buttons that were pressed down this frame        let pressed_this_frame = gamepad & (gamepad ^ previous);        PREVIOUS_GAMEPAD = gamepad;
        (pressed_this_frame, gamepad, previous)    };
    if pressed_this_frame & BUTTON_RIGHT != 0 {        trace("Right button was just pressed!");    }}

Mouse#

Mouse (or touchscreen) input is supported and will work for positions even outside of the game window on supported platforms. See the Memory Map reference for more details on MOUSE_X, MOUSE_Y, and MOUSE_BUTTONS.

On the example below, we can make a rectangle follow the mouse position and expand when clicked:

#[no_mangle]fn update() {    let mouse = unsafe { *MOUSE_BUTTONS };    let mouse_x = unsafe { *MOUSE_X };    let mouse_y = unsafe { *MOUSE_Y };
    if mouse & MOUSE_LEFT != 0 {        unsafe { *DRAW_COLORS = 4 }        rect(i32::from(mouse_x) - 8, i32::from(mouse_y) - 8, 16, 16);    } else {        unsafe { *DRAW_COLORS = 2 }        rect(i32::from(mouse_x) - 4, i32::from(mouse_y) - 4, 8, 8);    }}