Controls and input

Usage guide of input.actions, input.scheme, input.remap, input.virtualpad and input.pointer_lock: the chain from devices to actions to a control scheme, choosing a scheme by kind of game, settings and defaults, labels of keys, intents and composed actions, hooks, rebinding, the on-screen pad, mouse capture, engines.

Source: module-kits:kits/input_scheme/usage/en.mdView Markdown source

Also read offline — CLI: grip-forge docs inputMCP tool: gripforge_docs { "topic": "input" }

Controls and input of your game (input.* kits) #

How a game reads keyboard, mouse, gamepad and touch: named actions, a control scheme chosen by the kind of game, rebinding by the player, an on-screen pad and mouse capture. Readable offline: gripforge docs input, or the MCP tool gripforge_docs (topic input).

What it is for #

One chain, the same on every engine:

devices (keyboard, mouse, gamepad, touch)
  -> input.actions      named actions: pressed / justPressed / axis / stick
  -> input.scheme       the control scheme of the game, per device
  -> consumers          characters, units, interaction, vehicles: they read actions and intents, never a device

Kits never test a key code. A player can rebind controls, a touch screen feeds the same actions as a gamepad, and the prompt shown in your HUD always matches the real binding.

KitRoleIn a new game
input.actionsthe named actions, overrides, labels of keysyes
input.schemethe control scheme: what the pointer does, which actions exist and which are switched offchosen by the kind of game
input.remapthe Controls tab of the options: the player rebindsyes
input.virtualpadsticks and buttons on a touch screenno: add it to a game meant for touch
input.pointer_lockmouse capture for games that look with the mousewith the kits that look with the mouse

Turn it on #

gripforge_gamekit_install   { project_id: "gkp_…", id: "input.scheme" }
gripforge_gamekit_configure { project_id: "gkp_…", id: "input.scheme", config: { "scheme": "third_person" } }
gripforge_gamekit_install   { project_id: "gkp_…", id: "input.virtualpad" }       # a game for phones
gripforge_game_project      { project_id: "gkp_…", action: "controls", … }        # apply a scheme to the whole project

The control-scheme call rewrites the scheme entries of the input.actions overrides, the touch layout and the help bar in one step. Each kit's full reference is gripforge kit api <id>.

Choosing a scheme #

The game makes a gameplay choice and the kit implements it completely; what the scheme does not use stays off and invisible (no WASD in a click game on a mouse, no capture in a click game).

Kind of gameScheme
first-person shooter, survivalfps (WASD, captured mouse)
third-person action, open worldthird_person
MOBAmoba_click (right click moves and attacks, abilities toward the cursor)
dungeon crawler, action RPG seen from abovearpg_click
top-down or twin-stick shootertop_down
platformerplatformer (direct movement, a stick)
fightingfighting (one key per blow, no pointer)
racing, flightvehicle (throttle and brake, steering)
strategy, tower defenserts
point-and-click, turn-based, cards, puzzlepoint_click (cursor on screens)

input.actions #

KeyDefaultMeaning
gamepadtrueread gamepads (moddable)
deadzone0.15stick deadzone (moddable)
overrides[]{ action, keys, buttons?, axes?, buttonAxes?, disabled?, source? }: replace the keys or gamepad buttons of an action of any installed kit, or switch it off
scheme""the scheme the project started from (informative; what binds is overrides)
eventActionsnoneextra button names that publish input.action pressed and released edges (native player and Rust host)
  • Reading: pressed(action), justPressed(action), axis(action), stick(action) ({ x, y }), axis2D(xAction, yAction), sticks(). Presses shorter than a tick are kept, focus loss releases everything.
  • move is one 2D action. On a gamepad it takes a whole source (left stick, right stick, the d-pad or nothing), never four directions; on a keyboard it is the four keys of its two axes.
  • capture(kit) routes every action to one kit (used while driving).
  • Labels, the only way to show a key: labelFor(action, { lang?, device? }) (E, A/D, Space, - when unbound), promptFor(action, text?, opts?) ({ key, text, label: "[E] Interact", device, icon, control, bound }), keyInfo, actionText, device(), setDevice(keyboard | gamepad | auto). The binding is the effective one (kits, project overrides, then the player's own), the device is the one used last (input.device is emitted on a switch). Never write a key in a HUD, a dialogue or a tutorial.
  • Events: input.action { action, phase, pressed }, input.device { device }.

input.scheme #

KeyDefaultMeaning
scheme""the active scheme
allowed[]schemes the player may switch to in the settings
devices, autoSwitch[], truethe device follows the last one used
variants{}per-device departures from a scheme (see below)
followSeconds, followDistance0.15, 0.3a held click keeps the order following the cursor
holdRepeatSeconds0.25a held click on an entity issues the order again at this pace
attackInPlacetruein arpg_click, Shift held with the click attacks without walking
targetRange, targetCone, targetTags6, 60, ["enemy", "interactable"]picking a target with the stick on a gamepad
pickTags, pickRadius[], 0.9a click on the ground disc of an entity carrying one of these tags designates it (a downed teammate)
vehicleSchemevehiclepushed when the player sits in a vehicle
uiContextsmenu, dialogue, shop, storage, crafting, journal, photo, cinematic, end screeninterfaces that are an input context
  • Variant per device (schemeVariant): movement (direct, click, vehicle), pointer (captured, aim, orders, none), rightClick (context, aim, camera, none), frame (camera, screen, world), cameraRotate (mouse, right_drag, right_stick, none), aimAssist, plus actions brought back (enable) or switched off (disable).
  • Input contexts: while an interface is open only its own and the global actions are read; what is held is released when the context changes.
  • Intents: a click on the ground is a move intent, on an entity a target intent, a right click a context intent. A controller reads moveOrder() and calls clearMoveOrder(). target() is what the player designates (via pointer, aim or stick); gesture(action) says pressed, held, released or idle.
  • Composed actions: documents of the composites collection { id, kind, of, holdSeconds, maxSeconds, window }, kind press, release, hold, long, charge, tap, double, toggle, chord, sequence. The id becomes a button action every kit reads (justPressed("charge_shot")).
  • Changing scheme in play: set(id) (among allowed), push(id, owner) and pop(owner) (a vehicle, a map, a cinematic). Saved with the game.
  • It draws nothing: markers, highlights, prompts and cursors belong to the interaction display kits.
WhatNameDetail
Hookinput.scheme.change { scheme, previous, reason }scheme mutable, cancellable: redirect or refuse a change
Hookinput.intent { kind, action, point, entity, scheme, device }kind, point, entity mutable, cancellable: block walking in a dialogue, turn a click on a door into a context action
Eventsinput.scheme.changed, input.scheme.device, input.scheme.refused, input.context.changed, input.intent.issued
Registriesschemes, compositeslive; a mod adds or patches
Moddable configallowed, autoSwitch, variants, follow and target settings

input.remap #

The player rebinds from the Controls tab of the options screen: per action a primary and a secondary keyboard or mouse control and a gamepad button, "press a key" capture, conflicts reported, reset per action or all. It runs on the web, Godot, Unity and Unreal and applies the bindings to each engine's own input. Remove it only on request.

KeyDefaultMeaning
actions[]actions listed (empty: all)
exclude[]actions hidden from the tab
conflictsaskwhat happens when a control is used twice (moddable)
reserved["Escape"]controls that cannot be bound
gamepad, secondarytrue, trueshow the gamepad column and the secondary slot (moddable)

A stick action has one row (source, invert X, invert Y, deadzone 0 to 0.5); an axis on analog triggers has a gamepad slot on each of its two rows. Service: rows(lang), capture(row, slot), press(control), resolve(accept), cancel(), clear(row, slot), reset(row?), conflicts(), bindings(), sticks(), setStick, setInvert, setDeadzone. Events: remap.capture, remap.changed, remap.conflict, remap.cancelled, remap.stick, remap.reset.

input.virtualpad #

Sticks and buttons drawn on the screen that feed input.actions exactly like a physical gamepad, so every kit works unchanged on a phone. The whole pad is data. A control whose action no kit registered is not drawn, so one layout can serve several games.

A control: { id, kind (stick | button | dpad), action, x?, y?, mode, landscape, portrait, shape, opacity, icon, label, zone, directions, deadzone, visible }. Placement units are 1 % of the shorter side of the safe area (notch and system bars excluded), anchor is an edge, a corner or the centre, offset goes inward. A stick is fixed or floating; a button is hold or toggle; visible: "context" draws a button only while one of its icon rules holds.

KeyDefaultMeaning
modeautoauto (touch screens only), shown, hidden
layout, layoutsfoot, built-in foot and vehiclelayouts of the game; setLayout(id)
layoutRuleson foot / in a vehicle{ layout, tag } or { layout, var } picks the layout in use
icons, iconSet, iconSetsrules and a default seticons that follow the context ({ action, icon, var?, tag?, interaction? })
stylethemecolor, fill, active, border, opacity, scale
hideInMenus, hapticstrue, true

input.pointer_lock #

Captures the mouse for games that look around with it: the pointer is hidden and held, and its relative movement goes to the camera. The kit decides, the host executes; it never captures on a touch screen or when nothing wants it.

KeyDefaultMeaning
captureclickclick (the first capture waits for a click, a browser needs a gesture), play, manual
whenautoauto (a kit asked, a look service is mounted, or the scheme captures), always, requests
lookServices["camera.look"]capabilities that receive add(dx, dy)
cursorTopicsdialogue, shop, storage, crafting, journal, photo, end screenevent pairs that need the cursor
fullscreenGrace0.5seconds around entering fullscreen in which a lost capture is re-armed

The pointer is released by itself, with a reason in pointer.released (pause, menu, cursor, touch, unavailable, request, host). Several kits can ask at once; it is released when the last one calls release(owner). Service: capture(owner), release(owner), showCursor(owner), hideCursor(owner), isCaptured(), wanted(), available(), state(), movement(), consume().

Engines #

EngineInput
Web and native playerall five kits
Godotthe same model over the InputMap
Unitythe same model over the Input System (legacy Input Manager without it)
Unrealthe same model over Enhanced Input

Project overrides of other kits' actions and the labels are applied by the web and native players; the engine deliveries bake the key overrides of the kit's own actions.

When it does not do what you expect #

SymptomCause and fix
A key is shown in the HUD that is not the real onethe HUD writes the key itself: ask promptFor
The mouse is not capturedthe browser needs a click first (pointer.refused, gesture); a menu or a cursor topic is open; the scheme does not capture
WASD moves in a click gamethe scheme in input.actions.scheme is not the one in input.scheme: apply the project controls again
A touch control is missingno kit registered its action, or mode: "hidden"
A player cannot rebind an actionit is in exclude, or input.remap was removed