Gamepad¶
This page explains how to use gamepad (joystick) input in Kirikiri Z. For details on individual APIs, see the System class reference.
Pad numbers (logical index)¶
Every pad API takes a pad number no. The meaning of the number is the same throughout.
0… a special number meaning "the most recently operated pad". It automatically follows whichever pad was actually touched.1and above … physical pads in connection order (1= first,2= second, …). The number of connected pads is available fromSystem.getJoypadCount().
Key events for button presses (described below) always originate from pad 0 =
the most recently operated pad. With only one pad connected, 0 and 1 refer to
the same pad.
Switching of "the most recently operated pad" is decided by button / D-pad presses (small analog stick drift does not cause a switch).
Button input (key events)¶
Buttons arrive as key events. Receive VK_PAD1–VK_PAD12 / VK_PADLEFT–VK_PADDOWN
/ VK_PAD_L_* / VK_PAD_R_* in Window.onKeyDown / onKeyUp (see the
key code table for the list of key codes).
The current pressed state is also available from System.getKeyState(VK_PADn).
// Window を継承したクラスで
function onKeyDown(key, shift) {
if (key == VK_PAD1) { /* A ボタン */ }
if (key == VK_PADLEFT) { /* 十字左 */ }
}
The D-pad and left/right stick directions are quantized to 8 directions and arrive as the direction keys above (for raw stick values, see the next section).
VK_PAD1–VK_PAD4 are determined by the button label (SDL3 / generic build)¶
VK_PAD1 = label A (✕ on PlayStation), VK_PAD2 = B (○),
VK_PAD3 = X (□), VK_PAD4 = Y (△).
Nintendo-style controllers swap the positions of A/B and X/Y relative to Xbox-style ones, so assigning by position causes a mismatch like "the screen says 'Press A to confirm' but the button labeled B confirms". Assigning by label makes the on-screen prompt match the button actually pressed on any manufacturer's controller.
| Xbox-style | PlayStation-style | Nintendo-style | |
|---|---|---|---|
VK_PAD1 |
A (bottom) | ✕ (bottom) | A (right) |
VK_PAD2 |
B (right) | ○ (right) | B (bottom) |
VK_PAD3 |
X (left) | □ (left) | X (top) |
VK_PAD4 |
Y (top) | △ (top) | Y (left) |
Pad input in the native Windows (WINVER) build is based on XInput, where button labels are fixed to the Xbox layout, so position and label never disagree (the setting below does not exist in WINVER either).
To go back to the old position-based assignment, set
System.padButtonMapping = "position"
or specify the command-line option -padbuttons=position. Controllers whose labels
cannot be determined automatically fall back to position-based assignment.
This assignment happens at the single branch point "SDL button → VK_PAD*", so it
applies to both the game's padKeyMap and panel navigation in Elements dialogs.
The result is logged once per pad, which is useful for checking on real hardware.
Use VK_PAD_FACE_* for buttons you want to address by position¶
For the four face buttons, there are virtual keys that address them by position,
separate from the label-based VK_PAD1–VK_PAD4.
| Key code | Position | Xbox-style label | Nintendo-style label |
|---|---|---|---|
VK_PAD_FACE_SOUTH (0x1D4) |
Bottom | A | B |
VK_PAD_FACE_EAST (0x1D5) |
Right | B | A |
VK_PAD_FACE_WEST (0x1D6) |
Left | X | Y |
VK_PAD_FACE_NORTH (0x1D7) |
Top | Y | X |
A single press of a physical button delivers key events for both the label side and
the position side, so whoever assigns the controls can choose per button whether to
"align by label" or "align by position" (e.g. confirm on label A = VK_PAD1, and a
"menu opened with the top button" on VK_PAD_FACE_NORTH). Regardless of the
padButtonMapping setting, the position side always refers to the physical layout.
The button guide display side has the same two schemes (Elements' pad_icon accepts
both names, such as face_north and y). Pair assignments and displays that use the
same scheme.
⚠ Assigning both schemes to the same physical button fires twice per press. Since
both key events are delivered, if you assign both VK_PAD1 (label A) and
VK_PAD_FACE_SOUTH (bottom button) to "confirm", on an Xbox-style controller both
refer to the same physical button and the action runs twice. Pick one scheme per
button. If an Elements screen JSON mixes both schemes, a warning is logged when the
screen is built.
System.padStyle — which button artwork to use¶
The button labeling scheme of the connected pad is available from
System.padStyle (read-only). It is one of
"xbox" / "ps" / "switch", or an empty string when it cannot be determined. Use
it to choose the button artwork shown in control guides. Passing "auto" to
Elements' ElementsDialog.setPadTheme
selects the button artwork theme automatically based on this detection. The detection
watches the number of connected pads and their scheme and is redone the moment they
change, so you don't need to reopen the screen after plugging or unplugging a
controller (button artwork on a screen being displayed is replaced on the next draw).
On platforms where the scheme cannot be determined, it becomes "xbox" if at least
one pad is connected, and "keyboard" if none are.
Relationship with Elements dialogs¶
When a Dialog panel has keyboard focus,
VK_PAD* is consumed by widget operation in the panel (D-pad = focus navigation /
A = confirm / B = cancel). If you want "this pad button to always reach the game",
register it with
ElementsDialog.registerHotKey; it
then bypasses the dialog and goes straight to Window.onKeyDown (see the
Dialog guide for input delivery priority). Conversely,
to declare from the screen JSON side that "this input belongs to the game", specify
"action": "passthrough" in "bindings" (the panel passes it through without
consuming it; "none" differs in that it consumes the input and does nothing).
The core demo pad_advanced has a checkbox to turn this reservation on and off, so you
can compare on the spot how the same button switches between "reaching the game" and
"being absorbed by the panel".
Analog axes¶
Stick tilt and trigger depression are read with System.getPadAxis(no, axisId).
var x = System.getPadAxis(0, System.padAxisLeftX); // -1.0 〜 +1.0
var y = System.getPadAxis(0, System.padAxisLeftY); // -1.0 〜 +1.0 (下が正)
var t = System.getPadAxis(0, System.padAxisLeftTrigger); // 0.0 〜 +1.0
There are 6 axis IDs: System.padAxisLeftX / padAxisLeftY / padAxisRightX /
padAxisRightY / padAxisLeftTrigger / padAxisRightTrigger. The TJS global
constants paLeftX … paRightTrigger specify the same values. No dead zone is
applied, so handle it on the caller side as needed.
Values are not 0 even when idle
Because no dead zone is applied, measured values of around ±0.05 are returned
even when the stick is untouched. To avoid "the character moves even though I'm
not touching anything", discard small values on the caller side. You can see the
behavior in the core demo pad_advanced (stick grid + raw value display).
Connection detection / rumble¶
System.getJoypadCount()… number of physical pads connectedSystem.hasJoypad(no)… whether the given number is validSystem.getJoypadType(no)… model name string (environment-dependent: the recognized name in the SDL version,"XInput Controller"in the WINVER version)System.padStyle… button labeling scheme of the most recently operated pad ("xbox"/"ps"/"switch", or an empty string if unknown). Used to choose button artwork (previous section)System.onJoypadChange(no, name)… callback invoked when the identifier name of the most recently operated pad changes (nameis an empty string when no pad remains)System.rumblePad(no, low, high, durationMs)/System.stopRumblePad(no)… rumble (low/high are 0–255)
Disabling pad support (for support cases)¶
As a workaround when another device is mistakenly recognized as a gamepad and causes misbehavior, pad support can be disabled entirely. When disabled, neither state polling nor key event generation takes place.
- At runtime:
System.padEnabled = false;(re-enable with= true;) - At startup: command-line option
-joypad=no(command line)
An explicit setting via System.padEnabled takes precedence over the command line.
Platforms and implementation¶
Pad input consists of a logical management layer shared by all builds
(tTVPPadManager) with platform-specific backends on top.
- SDL3 / others: based on
SDL_Gamepad, supporting many models. If a bundledgamecontrollerdb.txtis present, it is loaded before pads are opened, so controllers missing from SDL's standard mappings can also be recognized. - WINVER (native Windows): based on XInput (up to 4 pads, rumble supported). Targets Xbox-style controllers; generic DirectInput pads are not supported.
For the debug display (button matrix + axis value overlay), see
PadOverlay. Internal engine design details are in
src/core/doc/Gamepad.md (SSOT).