Skip to content

InputManager

Handles input from physical buttons or keyboard (on PC).

Description

The InputManager polls configured pins (ESP32) or keyboard state (Native), handles debouncing, and tracks button states (Pressed, Released, Down, Clicked). It provides a unified input interface for both platforms.

The manager supports edge detection (just pressed/released) and continuous state (held down), making it suitable for both gameplay and UI navigation.

Namespace

namespace pixelroot32::input {
    class InputManager {
        // ...
    };
}

Inheritance

  • Base class: None (standalone class)
  • Used by: Engine (manages input manager instance)

Constructors

InputManager(const InputConfig& config)

Constructs the InputManager with a specific configuration.

Parameters: - config (const InputConfig&): The input configuration (pins, button count)

Example:

#include "input/InputManager.h"
#include "input/InputConfig.h"

// ESP32: Configure GPIO pins
pixelroot32::input::InputConfig inputConfig(6, 0, 2, 4, 5, 18, 19);
pixelroot32::input::InputManager inputManager(inputConfig);
inputManager.init();

// Native: Configure keyboard keys
// (Configuration handled differently on Native)

Public Methods

void init()

Initializes the input pins.

Returns: - void

Notes: - Must be called after construction and before use - Configures GPIO pins (ESP32) or keyboard state (Native) - Safe to call multiple times (idempotent) - Typically called automatically by Engine::init()

Example:

InputManager inputManager(inputConfig);
inputManager.init();  // Initialize before use

void update(unsigned long dt)

Updates input state by polling hardware pins (ESP32) or keyboard state (Native).

Parameters: - dt (unsigned long): Delta time in milliseconds

Returns: - void

Notes: - Must be called every frame for proper input detection - Handles debouncing automatically - Updates button states and edge detection - Typically called automatically by Engine::update()

ESP32 Example:

void update(unsigned long deltaTime) override {
    // Input is updated automatically by Engine
    // Access input via engine.getInputManager()
}

Native Example:

// On Native, update is called with keyboard state:
void update(unsigned long dt, const uint8_t* keyboardState);

bool isButtonPressed(uint8_t buttonIndex) const

Checks if a button was just pressed this frame.

Parameters: - buttonIndex (uint8_t): Index of the button to check (0-based)

Returns: - bool: true if the button transitioned from UP to DOWN this frame

Notes: - Returns true only on the frame the button was pressed - Useful for one-time actions (jump, shoot, menu select) - Resets automatically on next frame

Example:

auto& input = engine.getInputManager();

if (input.isButtonPressed(0)) {  // Button A (index 0)
    // Jump (only once per press)
    player->jump();
}

if (input.isButtonPressed(1)) {  // Button B (index 1)
    // Shoot (only once per press)
    player->shoot();
}

bool isButtonReleased(uint8_t buttonIndex) const

Checks if a button was just released this frame.

Parameters: - buttonIndex (uint8_t): Index of the button to check

Returns: - bool: true if the button transitioned from DOWN to UP this frame

Notes: - Returns true only on the frame the button was released - Useful for detecting button release events - Less commonly used than isButtonPressed()

Example:

auto& input = engine.getInputManager();

if (input.isButtonReleased(0)) {
    // Button A was just released
    player->stopCharging();
}

bool isButtonClicked(uint8_t buttonIndex) const

Checks if a button was clicked (pressed and released).

Parameters: - buttonIndex (uint8_t): Index of the button to check

Returns: - bool: true if the button was clicked (pressed then released)

Notes: - Returns true when button is released after being pressed - Useful for UI buttons and menu selection - Detects complete press-release cycle

Example:

auto& input = engine.getInputManager();

if (input.isButtonClicked(0)) {  // Button A clicked
    // Select menu item
    menu->select();
}

bool isButtonDown(uint8_t buttonIndex) const

Checks if a button is currently held down.

Parameters: - buttonIndex (uint8_t): Index of the button to check

Returns: - bool: true if the button is currently in the DOWN state

Notes: - Returns true for as long as the button is held - Useful for continuous actions (movement, charging) - Use with deltaTime for frame-rate independent movement

Example:

auto& input = engine.getInputManager();

float speed = 100.0f;  // pixels per second
float vx = 0.0f, vy = 0.0f;

if (input.isButtonDown(2)) {  // Left button
    vx = -speed;
}
if (input.isButtonDown(3)) {  // Right button
    vx = speed;
}
if (input.isButtonDown(0)) {  // Up button
    vy = -speed;
}
if (input.isButtonDown(1)) {  // Down button
    vy = speed;
}

// Apply movement (frame-rate independent)
x += (vx * deltaTime) / 1000.0f;
y += (vy * deltaTime) / 1000.0f;

Button Indices

Button indices are defined by the order in InputConfig:

Typical Mapping: - 0: Up / Button A - 1: Down / Button B - 2: Left - 3: Right - 4: Additional button 1 - 5: Additional button 2

Example:

// Configure 6 buttons: Up, Down, Left, Right, A, B
pixelroot32::input::InputConfig inputConfig(6, 
    GPIO_UP,    // Index 0
    GPIO_DOWN,  // Index 1
    GPIO_LEFT,  // Index 2
    GPIO_RIGHT, // Index 3
    GPIO_A,     // Index 4
    GPIO_B      // Index 5
);

// Use indices
if (input.isButtonDown(2)) {  // Left
    moveLeft();
}
if (input.isButtonPressed(4)) {  // A button
    jump();
}

Usage Example

#include "input/InputManager.h"
#include "core/Engine.h"

class PlayerController {
private:
    pixelroot32::core::Engine& engine;

public:
    PlayerController(pixelroot32::core::Engine& eng) : engine(eng) {}

    void update(unsigned long deltaTime) {
        auto& input = engine.getInputManager();

        // Movement (continuous)
        float speed = 150.0f;  // pixels per second
        float vx = 0.0f, vy = 0.0f;

        if (input.isButtonDown(3)) {  // Right
            vx = speed;
        }
        if (input.isButtonDown(2)) {  // Left
            vx = -speed;
        }
        if (input.isButtonDown(0)) {  // Up
            vy = -speed;
        }
        if (input.isButtonDown(1)) {  // Down
            vy = speed;
        }

        // Apply movement
        playerX += (vx * deltaTime) / 1000.0f;
        playerY += (vy * deltaTime) / 1000.0f;

        // Actions (one-time)
        if (input.isButtonPressed(4)) {  // A button
            player->jump();
        }

        if (input.isButtonPressed(5)) {  // B button
            player->shoot();
        }
    }
};

Input State Comparison

Method Returns true when Use Case
isButtonPressed() Button just pressed this frame One-time actions (jump, shoot)
isButtonReleased() Button just released this frame Release events (stop charging)
isButtonClicked() Button pressed then released UI buttons, menu selection
isButtonDown() Button currently held Continuous actions (movement)

Performance Considerations

  • Update frequency: update() must be called every frame
  • Debouncing: Handled automatically, no performance impact
  • State queries: All query methods are fast (inline accessors)
  • Memory: Button state arrays are small and efficient

ESP32 Considerations

  • GPIO pins: Configure pins in InputConfig
  • Pull-up/pull-down: Ensure proper resistor configuration
  • Debouncing: Hardware debouncing recommended for noisy buttons
  • Pin limits: Some ESP32 pins have restrictions (check datasheet)

Native Considerations

  • Keyboard mapping: Uses SDL scancodes
  • Key detection: Automatically handles keyboard state
  • Multiple keys: Can detect multiple keys simultaneously

See Also