Input
WolfEngine's input system handles button polling and analog joystick reading with built-in software debouncing. It supports up to 4 controllers simultaneously, each with buttons wired directly to GPIO pins, buttons routed through an I2C expander (PCF8574, PCF8575, or MCP23017), and an analog joystick via ADC â all configured from a single settings file.
Accessing Input
Input is available anywhere in game code through the global Input() function. First get a reference to the controller you want to query, then call methods on it:
Controller* controller = nullptr;
void Start() override {
controller = Input().getController(0); // player 1
}
void Update() override {
if (controller && controller->getButtonDown<Button::A>()) {
// do something
}
}
Note: Always store the controller pointer in
Start()rather than callinggetController()every frame.
Controllers
WolfEngine supports up to 4 simultaneous controllers. Each controller is configured independently in Input Settings.
Controller* p1 = Input().getController(0); // player 1
Controller* p2 = Input().getController(1); // player 2
getController() expects indices in the 0..3 range. Passing an out-of-range index will trigger a debug assert. It returns nullptr for disabled controller slots. Always null-check before use if unsure:
Controller* p2 = Input().getController(1);
if (p2) {
p2->getButtonDown<Button::A>();
}
Buttons
WolfEngine supports 10 buttons per controller:
| Button | Enum |
|---|---|
| A | Button::A |
| B | Button::B |
| C | Button::C |
| D | Button::D |
| E | Button::E |
| F | Button::F |
| G | Button::G |
| H | Button::H |
| I | Button::I |
| J | Button::J |
Each button can be wired to a direct GPIO pin, an expander pin, or left unassigned â all configured in Input Settings.
Note: Button labels have no predefined meaning in the engine. They are just identifiers. Your controller may have ABXY, directions, or any other layout â wire them however you like and assign meaning in your game code.
Button Queries
getButton<Button::B>()
Returns true every frame the button is held down.
if (controller && controller->getButton<Button::B>()) {
player.accelerate();
}
getButtonDown<Button::B>()
Returns true only on the single frame the button is first pressed. Use this for one-shot actions.
if (controller && controller->getButtonDown<Button::A>()) {
player.jump();
}
getButtonUp<Button::B>()
Returns true only on the single frame the button is released.
if (controller && controller->getButtonUp<Button::A>()) {
player.stopCharging();
}
Analog Joystick
The joystick returns a normalized float value per axis in the range -1.0 to +1.0. Values inside the dead zone return exactly 0.0 to prevent stick drift.
float x = controller->getAxis(JoyAxis::X);
float y = controller->getAxis(JoyAxis::Y);
| Value | Meaning |
|---|---|
-1.0 |
Full negative (left / down) |
0.0 |
Center or inside dead zone |
+1.0 |
Full positive (right / up) |
Dead zone size and axis calibration are configured per controller in Input Settings. If a joystick axis is disabled, getAxis() always returns 0.0.
Supported Expanders
Buttons can be routed through any of the following I2C expanders instead of direct GPIO:
| Chip | Type | Pins | Address Range |
|---|---|---|---|
| PCF8574 | 8-bit, no registers | 0â7 | 0x20â0x27 |
| PCF8575 | 16-bit, no registers | 0â15 | 0x20â0x27 |
| MCP23017 | 16-bit, register-based | 0â15 | 0x20â0x27 |
Expander type and address are configured per controller in Input Settings.
Debouncing
All buttons across all controllers are automatically debounced in software. A button state change is only committed after the signal has remained stable for the duration defined by debounceMs in Input Settings. This prevents noise and contact bounce from registering false presses. Debounce timing is engine-wide and applies equally to all controllers.
Configuration
All input routing, expander setup, joystick calibration, and controller enabling is done in WE_Settings.hpp. See Input Settings for the full reference.