Input System Guide¶
Overview¶
The PixelRoot32 input system provides unified button handling across ESP32 and PC (native/SDL2) platforms. It abstracts hardware-specific input (GPIO pins on ESP32, keyboard on PC) into a consistent API for game development.
Architecture¶
The input system is built around the InputManager class which: - Polls button states every frame - Tracks button presses, releases, and hold states - Provides both polling and event-based interfaces - Works identically across all supported platforms
Button Mapping¶
PixelRoot32 uses a standard 6-button layout:
| Button Index | Name | ESP32 (GPIO) | PC (SDL Key) |
|---|---|---|---|
| 0 | UP | Configurable | SDL_SCANCODE_UP |
| 1 | DOWN | Configurable | SDL_SCANCODE_DOWN |
| 2 | LEFT | Configurable | SDL_SCANCODE_LEFT |
| 3 | RIGHT | Configurable | SDL_SCANCODE_RIGHT |
| 4 | A (Action) | Configurable | SDL_SCANCODE_SPACE |
| 5 | B (Action) | Configurable | SDL_SCANCODE_RETURN |
Configuration¶
ESP32 Hardware Input¶
#include <input/InputConfig.h>
// Configure 6 buttons with specific GPIO pins
pixelroot32::input::InputConfig inputConfig(
6, // button count
32, // UP pin
27, // DOWN pin
33, // LEFT pin
14, // RIGHT pin
13, // A button pin
12 // B button pin
);
// Create engine with input config
pixelroot32::core::Engine engine(displayConfig, inputConfig, audioConfig);
PC Native Input (SDL2)¶
#include <input/InputConfig.h>
// Configure with SDL scancodes
pixelroot32::input::InputConfig inputConfig(
6, // button count
SDL_SCANCODE_UP, // UP
SDL_SCANCODE_DOWN, // DOWN
SDL_SCANCODE_LEFT, // LEFT
SDL_SCANCODE_RIGHT, // RIGHT
SDL_SCANCODE_SPACE, // A button
SDL_SCANCODE_RETURN // B button
);
Input Patterns¶
1. Polling (Every Frame)¶
Check button state in your update() method:
void MyScene::update(unsigned long deltaTime) {
auto& input = engine.getInputManager();
// Check if button is currently pressed
if (input.isButtonPressed(0)) { // UP
player->move(0, -speed * deltaTime);
}
if (input.isButtonPressed(2)) { // LEFT
player->move(-speed * deltaTime, 0);
}
// Check for "just pressed" (single trigger)
if (input.isButtonJustPressed(4)) { // A button
player->jump();
}
// Check for "just released"
if (input.isButtonJustReleased(4)) {
player->endJump();
}
}
2. State-Based Input¶
Query specific button states:
auto& input = engine.getInputManager();
// Was button pressed this frame?
bool pressed = input.isButtonPressed(buttonIndex);
// Was button just pressed this frame (transition from released)?
bool justPressed = input.isButtonJustPressed(buttonIndex);
// Was button just released this frame?
bool justReleased = input.isButtonJustReleased(buttonIndex);
3. D-Pad Movement Helper¶
Common pattern for directional movement:
void updatePlayerMovement() {
auto& input = engine.getInputManager();
float dx = 0, dy = 0;
float speed = 100.0f; // pixels per second
if (input.isButtonPressed(0)) dy -= 1; // UP
if (input.isButtonPressed(1)) dy += 1; // DOWN
if (input.isButtonPressed(2)) dx -= 1; // LEFT
if (input.isButtonPressed(3)) dx += 1; // RIGHT
// Normalize diagonal movement
if (dx != 0 && dy != 0) {
dx *= 0.707f; // 1/sqrt(2)
dy *= 0.707f;
}
player->velocity.x = dx * speed;
player->velocity.y = dy * speed;
}
Platform Differences¶
ESP32¶
- Debouncing: Handled automatically by the
OneButtonlibrary - Pull-up: Configure your GPIO pins with internal pull-up resistors
- Hardware: Physical buttons connected to specified GPIO pins
PC Native¶
- Keyboard Focus: Input works when SDL window has focus
- Key Repeat: Disabled by default (use
isButtonPressedfor repeat) - Multiple Keys: All 6 buttons can be pressed simultaneously
Best Practices¶
1. Use isButtonJustPressed for Actions¶
// ✅ GOOD: Single jump per press
if (input.isButtonJustPressed(4)) {
player->jump();
}
// ❌ BAD: Would jump every frame while held
if (input.isButtonPressed(4)) {
player->jump();
}
2. Normalize Diagonal Movement¶
// Always normalize to prevent faster diagonal movement
if (dx != 0 && dy != 0) {
dx *= 0.707f;
dy *= 0.707f;
}
3. Handle Menu Navigation¶
// Menu navigation with repeat delay
unsigned long lastNavTime = 0;
const unsigned long NAV_DELAY = 200; // ms
void updateMenu() {
auto& input = engine.getInputManager();
unsigned long now = millis();
if (now - lastNavTime > NAV_DELAY) {
if (input.isButtonPressed(0)) { // UP
menu->moveSelection(-1);
lastNavTime = now;
}
if (input.isButtonPressed(1)) { // DOWN
menu->moveSelection(1);
lastNavTime = now;
}
}
// Select with A button
if (input.isButtonJustPressed(4)) {
menu->select();
}
}
4. Virtual Input (Code-Driven)¶
Create virtual button presses for AI or demo modes:
// Simulate button press programmatically
void triggerVirtualInput(int buttonIndex) {
// Access internal state (advanced usage)
// Useful for AI players or recorded replays
}
Advanced Features¶
Input Buffering¶
For fighting games or precise platformers, you may want to buffer inputs:
struct InputBuffer {
static const int BUFFER_SIZE = 8;
struct BufferedInput {
int button;
unsigned long time;
};
BufferedInput buffer[BUFFER_SIZE];
int count = 0;
void record(int button) {
if (count < BUFFER_SIZE) {
buffer[count++] = {button, millis()};
}
}
bool checkCombo(const int* combo, int length, unsigned long window) {
// Check if combo was entered within time window
// ... implementation
}
};
Axis-Based Input (Analog)¶
For analog sticks or variable pressure:
// Get intensity if using analog input (advanced)
float getAnalogValue(int buttonIndex) {
// Returns 0.0 to 1.0 for analog buttons
// Currently not implemented in base InputManager
return input.isButtonPressed(buttonIndex) ? 1.0f : 0.0f;
}
Troubleshooting¶
Buttons Not Responding (ESP32)¶
- Check wiring: Verify buttons are connected to correct GPIO pins
- Pull-up resistors: Enable internal pull-ups or add external 10kΩ resistors
- Pin configuration: Double-check
InputConfigpin numbers - Ground connection: Ensure buttons connect to GND when pressed
Keys Not Working (PC)¶
- Window focus: Click on the game window to ensure it has focus
- SDL initialization: Verify SDL is properly initialized
- Scancode vs Keycode: Use
SDL_SCANCODE_*notSDLK_*
Multiple Button Presses¶
If you need more than 6 buttons, extend the input system:
// Custom input configuration with more buttons
class ExtendedInputConfig : public InputConfig {
// Add support for additional buttons
// Requires modifying the engine's input polling
};
API Reference¶
See API Reference - InputManager for complete method documentation.
Examples¶
- Pong: Basic D-pad control (2 players)
- Space Invaders: Simple movement + fire button
- Snake: Directional control with wrap-around
See also: