Skip to main content

Input

FlixelGDX exposes two layers of input:

  • Raw input — direct per-frame queries against Flixel.keys, Flixel.mouse, and Flixel.gamepads. Use these when you need to check a specific physical button immediately.
  • Action sets — named logical actions (Jump, Attack, Move) that map to one or more hardware bindings. Your game logic asks "is Jump pressed?" and the framework resolves whether that came from a keyboard, a controller, or a touch screen.

For anything beyond a quick prototype, action sets are the recommended approach. They keep all binding logic in one place and make rebinding trivial without touching game code.


Raw input

Keyboard — Flixel.keys

Key codes are plain int constants on FlixelKey (FlixelKey.SPACE, FlixelKey.LEFT, etc.). The special value FlixelKey.ANY matches any key.

// True every frame the key is held down.
if (Flixel.keys.pressed(FlixelKey.SPACE)) { ... }

// True only on the single frame the key went down.
if (Flixel.keys.justPressed(FlixelKey.ENTER)) { ... }

// True only on the single frame the key was released.
if (Flixel.keys.justReleased(FlixelKey.ESCAPE)) { ... }

// True when any of the listed keys is held (OR check, avoids nested ifs).
if (Flixel.keys.anyPressed(FlixelKey.LEFT, FlixelKey.A)) { ... }
if (Flixel.keys.anyJustPressed(FlixelKey.SPACE, FlixelKey.W)) { ... }
if (Flixel.keys.anyJustReleased(FlixelKey.SPACE, FlixelKey.W)) { ... }

// Key held the longest (first chronologically among currently held keys).
int first = Flixel.keys.firstPressed();

// First key to go down this frame.
int firstDown = Flixel.keys.firstJustPressed();

// First key released this frame.
int firstUp = Flixel.keys.firstJustReleased();

firstPressed(), firstJustPressed(), and firstJustReleased() all return FlixelKey.NONE when no key matches. They are useful for key-rebinding screens where you want to capture whatever the player pressed without checking individual codes.


Mouse — Flixel.mouse

Mouse buttons are int constants from FlixelMouseButton (FlixelMouseButton.LEFT, FlixelMouseButton.RIGHT, FlixelMouseButton.MIDDLE).

// Button state queries. Same pressed/justPressed/justReleased contract as the keyboard.
if (Flixel.mouse.pressed(FlixelMouseButton.LEFT)) { ... }
if (Flixel.mouse.justPressed(FlixelMouseButton.LEFT)) { ... }
if (Flixel.mouse.justReleased(FlixelMouseButton.RIGHT)) { ... }

// Cursor position in world coordinates (unprojected through the active camera).
float wx = Flixel.mouse.getWorldX();
float wy = Flixel.mouse.getWorldY();

// Cursor position in raw screen pixels (top-left origin).
int sx = Flixel.mouse.getScreenX();
int sy = Flixel.mouse.getScreenY();

// Scroll deltas accumulated this frame (reset by the framework at end of frame).
float scrollY = Flixel.mouse.getScrollDeltaY(); // Typical scroll wheel up/down.
float scrollX = Flixel.mouse.getScrollDeltaX(); // Horizontal scroll (trackpad, etc.).

// Hover check: true when the cursor world position is inside obj's bounding box.
if (Flixel.mouse.overlap(button)) { ... }

getScrollDeltaY() is the primary scroll axis — positive when the wheel scrolls down, negative when it scrolls up (exact sign is OS and backend dependent; verify on your target platform). Both deltas reset to zero at the end of each frame so you only see movement that happened this tick.


Gamepad — Flixel.gamepads

The gamepad system is disabled by default. Set enabled = true once, before your game loop starts, to opt in:

// Opt in before the game loop starts (e.g. in FlixelGame's constructor).
Flixel.gamepads.enabled = true;
Verifying the gamepad system initialized

After you enable the framework's gamepad system, if you see a log message in the console that reads something like [Controllers] added manager for application, 1 managers active, then that means the gamepad system was successfully installed and is ready to be used.

Button codes are constants on FlixelGamepadInput (FlixelGamepadInput.A, FlixelGamepadInput.X, FlixelGamepadInput.START, etc.). The first argument is the zero-based controller slot index:

// Button queries for controller in slot 0.
if (Flixel.gamepads.pressed(0, FlixelGamepadInput.A)) { ... }
if (Flixel.gamepads.justPressed(0, FlixelGamepadInput.A)) { ... }
if (Flixel.gamepads.justReleased(0, FlixelGamepadInput.B)) { ... }

// Stick axis value in [-1, 1] for controller 0 (dead zone applied if globalDeadZone is set).
float lx = Flixel.gamepads.getAxis(0, FlixelGamepadInput.AXIS_LEFT_X);
float ly = Flixel.gamepads.getAxis(0, FlixelGamepadInput.AXIS_LEFT_Y);

// Any-controller variants: true if any connected controller matches.
if (Flixel.gamepads.anyPressed(FlixelGamepadInput.START)) { ... }
if (Flixel.gamepads.anyJustPressed(FlixelGamepadInput.A)) { ... }
if (Flixel.gamepads.anyJustReleased(FlixelGamepadInput.A)) { ... }

// Optional global dead zone applied to all getAxis() calls.
Flixel.gamepads.globalDeadZone = 0.15f;

Axis constants are AXIS_LEFT_X, AXIS_LEFT_Y, AXIS_RIGHT_X, and AXIS_RIGHT_Y. The Y axes are already flipped from screen-space to math-space (up = positive) when used through FlixelActionAnalog axis bindings; raw getAxis() returns the hardware value directly.

Connection events

Listen to deviceConnected and deviceDisconnected to react when controllers are plugged or unplugged at runtime:

Flixel.gamepads.deviceConnected.addListener(event -> {
Flixel.info("Controller " + event.gamepadId() + " connected: " + event.model());
});

Flixel.gamepads.deviceDisconnected.addListener(event -> {
showPauseMenu("Controller disconnected!");
});

Action sets

Action sets decouple game logic from hardware. Instead of checking FlixelKey.SPACE directly, you define a Jump action and let the action set resolve which physical inputs trigger it. This makes rebinding a data change rather than a code change, and lets the same logic work across keyboard, gamepad, and touch without any branching in your update loop.

Defining an action set

Extend FlixelActionSet and declare each action as a field. In the constructor, register bindings using FlixelInputBinding (for digital actions) and FlixelAnalogAxisBinding (for analog actions):

public class PlayerControls extends FlixelActionSet {

public final FlixelActionDigital jump;
public final FlixelActionDigital attack;
public final FlixelActionAnalog move;

public PlayerControls() {
// add(...) registers the action with the set so the framework ticks it each frame.
jump = new FlixelActionDigital("jump");
jump.addBinding(FlixelInputBinding.key(FlixelKey.SPACE));
jump.addBinding(FlixelInputBinding.key(FlixelKey.W));
jump.addBinding(FlixelInputBinding.gamepadButton(0, FlixelGamepadInput.A));
add(jump);

attack = new FlixelActionDigital("attack");
attack.addBinding(FlixelInputBinding.key(FlixelKey.Z));
attack.addBinding(FlixelInputBinding.gamepadButton(0, FlixelGamepadInput.X));
add(attack);

// Analog actions accumulate key halves and stick axes into a normalized [-1, 1] vector.
move = new FlixelActionAnalog("move");
move.addAxisBinding(FlixelAnalogAxisBinding.negXKey(FlixelKey.LEFT));
move.addAxisBinding(FlixelAnalogAxisBinding.posXKey(FlixelKey.RIGHT));
move.addAxisBinding(FlixelAnalogAxisBinding.negYKey(FlixelKey.UP));
move.addAxisBinding(FlixelAnalogAxisBinding.posYKey(FlixelKey.DOWN));
move.addAxisBinding(FlixelAnalogAxisBinding.gamepadAxisX(0, FlixelGamepadInput.AXIS_LEFT_X));
move.addAxisBinding(FlixelAnalogAxisBinding.gamepadAxisY(0, FlixelGamepadInput.AXIS_LEFT_Y));
add(move);
}
}

FlixelActionSet is registered with FlixelActionSets automatically on construction, so the framework calls update and endFrame on it every game loop tick. Call destroy() when the set is no longer needed (for example, on state transition) to unregister it and stop stale reads.


Digital action methods

Instantiate the set once (typically in your state's create) and poll it in update:

private final PlayerControls controls = new PlayerControls();

@Override
public void update(float elapsed) {
super.update(elapsed);

// True every frame the action is active.
if (controls.jump.pressed()) { ... }

// True only on the single frame the action became active.
if (controls.jump.justPressed()) { ... }

// True only on the single frame the action was released.
if (controls.jump.justReleased()) { ... }

// True on the initial press, then again after holdDelay seconds, then every holdInterval
// seconds while held. Use for navigation or incrementing values that should auto-repeat.
if (controls.jump.held()) { ... }
}

held() fires on the initial press and then continues firing at a fixed interval while held. It is intended for UI navigation and similar cases where holding a button should keep triggering the action rather than requiring repeated taps.


Analog action methods

Analog actions expose a two-axis normalized vector that combines all active bindings. Key bindings contribute exactly +1 or -1 per axis; stick bindings contribute smooth hardware values. The result is clamped to length 1 so diagonals never exceed unit speed:

// Current axis values, each in [-1, 1].
float x = controls.move.getX();
float y = controls.move.getY();

// Axis values from the previous frame, available after endFrame().
float prevX = controls.move.getPrevX();
float prevY = controls.move.getPrevY();

// True when either axis is non-zero (the vector has any length).
if (controls.move.moved()) { ... }

// flicked(): true for exactly one frame when the stick crosses the flick threshold
// from below. Returns false while the stick stays past the threshold. Good for
// single-action menu navigation where each deflection triggers exactly one move.
if (controls.navigate.flicked()) {
if (controls.navigate.getY() > 0) selectPreviousItem();
else if (controls.navigate.getY() < 0) selectNextItem();
}

// flickedRepeating(): same as flicked() on the initial cross, then fires again after
// holdDelay seconds if the stick is still past the threshold, and every holdInterval
// seconds after that. Use this when holding the stick should keep scrolling.
if (controls.navigate.flickedRepeating()) {
if (controls.navigate.getY() > 0) selectPreviousItem();
else if (controls.navigate.getY() < 0) selectNextItem();
}

// Adjust the stick magnitude required to trigger flicked()/flickedRepeating().
// Default is 0.3 (30% deflection). Key bindings contribute +-1 so they always exceed it.
controls.navigate.setFlickThreshold(0.5f);

flicked() mirrors the single-frame contract of justPressed() on a digital action: one deflection, one event. flickedRepeating() mirrors repeated(). Choose whichever matches whether your UI should scroll continuously when the stick is held.