Input Actions¶
Input actions decouple your game logic from physical inputs. A "Jump" action can be bound to the spacebar, a gamepad button, and a trigger simultaneously — the action fires when any binding is active, and bindings can be changed at runtime without touching game code.
Quick Start¶
using Brine2D.Input;
// Create an action with multiple bindings
var jumpAction = new InputAction("Jump",
new KeyBinding(Key.Space),
new GamepadButtonBinding(GamepadButton.A));
protected override void OnUpdate(GameTime gameTime)
{
if (jumpAction.IsPressed(Input)) Jump();
}
InputAction¶
An InputAction is a named logical action with one or more InputBinding objects.
var moveRight = new InputAction("MoveRight",
new KeyAxisBinding(Key.D, Key.A), // D = positive, A = negative
new GamepadAxisBinding(GamepadAxis.LeftX)); // Gamepad left stick X
// Query the action
bool held = moveRight.IsDown(Input);
bool pressed = moveRight.IsPressed(Input);
bool release = moveRight.IsReleased(Input);
float value = moveRight.ReadValue(Input); // -1 to 1
// Read two actions as a Vector2 (X + Y)
var moveX = new InputAction("MoveX", new KeyAxisBinding(Key.D, Key.A));
var moveY = new InputAction("MoveY", new KeyAxisBinding(Key.S, Key.W));
Vector2 direction = moveX.ReadVector2(Input, moveY);
Runtime Rebinding¶
// Add a new binding
jumpAction.AddBinding(new GamepadButtonBinding(GamepadButton.B));
// Remove a specific binding
jumpAction.RemoveBinding(existingBinding);
// Clear all bindings
jumpAction.ClearBindings();
InputActionMap¶
An InputActionMap is a named collection of actions. Typical usage is one map per game context (e.g., "Player", "UI", "Vehicle"). The whole map can be disabled without unregistering individual actions.
var playerMap = new InputActionMap("Player");
var jump = new InputAction("Jump", new KeyBinding(Key.Space));
var shoot = new InputAction("Shoot", new MouseButtonBinding(MouseButton.Left));
var moveX = new InputAction("MoveX", new KeyAxisBinding(Key.D, Key.A));
var moveY = new InputAction("MoveY", new KeyAxisBinding(Key.S, Key.W));
playerMap.AddAction(jump);
playerMap.AddAction(shoot);
playerMap.AddAction(moveX);
playerMap.AddAction(moveY);
Querying a Map¶
// By action name
bool jumping = playerMap.IsDown("Jump", Input);
bool shooting = playerMap.IsPressed("Shoot", Input);
float xAxis = playerMap.ReadValue("MoveX", Input);
// Or get the action object directly
var jumpAction = playerMap["Jump"];
jumpAction.IsPressed(Input);
// Safe lookup (returns false if not found)
if (playerMap.TryGetAction("Jump", out var action))
action.IsPressed(Input);
Disabling a Map¶
// Disable the entire map (all queries return false/zero)
playerMap.Enabled = false;
// Re-enable it
playerMap.Enabled = true;
Binding Types¶
KeyBinding -- Single Key¶
new KeyBinding(Key.Space)
KeyAxisBinding -- Two Keys as an Axis (-1 to 1)¶
new KeyAxisBinding(positive: Key.D, negative: Key.A)
// ReadValue returns 1 when D held, -1 when A held, 0 when neither or both
CompositeKeyBinding -- All Keys Held Simultaneously¶
new CompositeKeyBinding(Key.LeftControl, Key.S) // Ctrl+S
// IsPressed fires when the last key completes the combo
MouseButtonBinding -- Single Mouse Button¶
new MouseButtonBinding(MouseButton.Left)
GamepadButtonBinding -- Single Gamepad Button¶
new GamepadButtonBinding(GamepadButton.A)
new GamepadButtonBinding(GamepadButton.Start, gamepadIndex: 1) // Player 2
GamepadAxisBinding -- Raw Gamepad Axis with Deadzone¶
new GamepadAxisBinding(GamepadAxis.LeftX)
// ReadValue rescales past the deadzone to 0--1
Note
GamepadAxisBinding uses a per-axis (not radial) deadzone check. For consistent 2D
directional input on a stick, prefer GamepadStickBinding.
GamepadStickBinding -- Stick Axis with Radial Deadzone¶
new GamepadStickBinding(GamepadStick.Left, GamepadStickAxis.X)
new GamepadStickBinding(GamepadStick.Right, GamepadStickAxis.Y, gamepadIndex: 0)
// Applies the same radial deadzone as GetGamepadLeftStick() / GetGamepadRightStick()
GamepadTriggerBinding -- Trigger (0 to 1)¶
new GamepadTriggerBinding(GamepadAxis.RightTrigger)
// IsPressed fires when trigger crosses the deadzone threshold
Full Example: Player Action Map¶
public class PlayerActions
{
public InputActionMap Map { get; }
public InputAction Jump { get; }
public InputAction MoveX { get; }
public InputAction MoveY { get; }
public InputAction Fire { get; }
public PlayerActions()
{
Map = new InputActionMap("Player");
Jump = new InputAction("Jump",
new KeyBinding(Key.Space),
new GamepadButtonBinding(GamepadButton.A));
MoveX = new InputAction("MoveX",
new KeyAxisBinding(Key.D, Key.A),
new GamepadStickBinding(GamepadStick.Left, GamepadStickAxis.X));
MoveY = new InputAction("MoveY",
new KeyAxisBinding(Key.S, Key.W),
new GamepadStickBinding(GamepadStick.Left, GamepadStickAxis.Y));
Fire = new InputAction("Fire",
new MouseButtonBinding(MouseButton.Left),
new GamepadTriggerBinding(GamepadAxis.RightTrigger));
Map.AddAction(Jump);
Map.AddAction(MoveX);
Map.AddAction(MoveY);
Map.AddAction(Fire);
}
}
public class PlayerScene : Scene
{
private readonly PlayerActions _actions = new();
protected override void OnUpdate(GameTime gameTime)
{
var dt = (float)gameTime.DeltaTime;
var direction = MoveX.ReadVector2(Input, MoveY);
if (direction != Vector2.Zero)
direction = Vector2.Normalize(direction);
_position += direction * _speed * dt;
if (Jump.IsPressed(Input)) Jump();
if (Fire.IsDown(Input)) Fire();
}
private InputAction MoveX => _actions.MoveX;
private InputAction MoveY => _actions.MoveY;
private InputAction Jump => _actions.Jump;
private InputAction Fire => _actions.Fire;
}