Skip to Content
This documentation is provided with the HEAT environment and describes this HEAT instance.
AdministratorsHEAT CaptureCapture modulesGame controller input moduleData produced by the game controller input module

Data produced by the game controller input module

One entity per connected, matching device, published every capture tick regardless of activity - even a device nobody is touching still gets its EntityState in the frame. Whether that entity also carries any events that tick depends on TrackEdgeDetection and SendAxisData below.

Entity

FieldValue
NameThe DirectInput driver’s reported device name (for example "Logitech G29 Racing Wheel"), or a synthetic "XInput Gamepad (Player N)" / "XInput Controller (Player N)" for an XInput slot
IdA hash of the device’s instance GUID (DirectInput) or of the synthetic per-slot GUID (XInput)

Button events

Each button transition is one ButtonPressEvent, with button_name and state mapped from this module’s own four internal states:

Internal stateMeaningPublished state
DownFirst tick the button reads pressedBUTTON_STATE_PRESSED
HoldHeld from a previous tickBUTTON_STATE_HOLD
ReleasedFirst tick the button reads releasedBUTTON_STATE_RELEASED
UpNot pressedNot published at all

A button held down publishes a fresh Hold event every tick for as long as it stays pressed, not once. Filter to BUTTON_STATE_PRESSED if only the rising edge matters to your use case.

Button names: DirectInput resolves whatever name the driver reports ("Button 0", "Button 1", … for an unbranded pad; a real name for some branded controllers). XInput uses a fixed table: A, B, X, Y, LB, RB, Back, Start, LS, RS, DPad Up, DPad Down, DPad Left, DPad Right.

Axis events

When SendAxisData is on (default), every active device also publishes one JsonEventData named ControllerAxes every tick, unconditionally

  • even a device nobody is touching still sends its (unchanging) axis values every tick. This is flagged as an open question in the module’s own source rather than a settled design decision; if the event volume from an idle controller is a problem for your use case, disabling SendAxisData is the only control available today. The JSON shape is not the same between the two device types:

DirectInput - one field per axis the device actually reports, all normalized to -1..1 except POV switches:

FieldMeaning
x, y, zPrimary axes
rx, ry, rzRotational axes
slider0, slider1, …Slider controls, if the device has any
pov0, pov1, …Point-of-view (hat switch) controllers, in degrees / 100 as a fraction 0..1, or -1 when centered/not pressed

XInput - always exactly these six fields, all normalized to -1..1 (triggers 0..1):

FieldMeaning
left_x, left_yLeft stick
right_x, right_yRight stick
left_trigger, right_triggerAnalog triggers

A consumer reading ControllerAxes must key its parsing off which entity (and therefore which device type) the event belongs to; the two schemas share no field names.

Timing

Button edges and axis values are only computed and published when this module’s own capture call runs, at the Interval setting’s cadence - see Configure for why this, not PollIntervalMs, is what actually bounds how quickly a button press is seen.

Opens your browser's print dialog. Choose "Save as PDF" as the destination.