Skip to main content

Input

Irregular • File: InputCapture.csv • Writer: KeyCodeDataWriter.cs

At a glance

Input captures key input events for keys configured on the KeyCodeDataCapture asset. Emits on key-down, key-up, and optionally key-held (every N samples while held). Each row records the key code and the event type. Stamped at the frame the input was polled.

Use Input for:

  • Reaction-time analyses (stimulus → key press).
  • Response logging (which key did they press?).
  • Keyboard / controller event timelines.

When it writes

KeyCodeInputObserver detects configured key events and fires OnInputEvent. The writer picks that up via RecordEntry. Timing is stamped at the frame of detection — so FrameNumber is the frame on which Unity reported the input state change.

File & location

  • Default: InputCapture.csv in the run directory.
  • Writer: KeyCodeDataWriter.cs in Runtime/DataCapture/InputLogging/.
  • Observer: KeyCodeInputObserver.cs (same directory).
  • Observation: Runtime/DataCapture/DataModels/KeyCodeObservation.cs.
  • Config SO: KeyCodeDataCapture.cs.

Configuration

KeyCodeDataCapture SO fields:

FieldWhat it does
enabledLoggingMaster enable/disable.
fileNameOverride the default name.
flushEveryNRowsBatched flush cadence. Default 30.
writeHeaderWrite the header row on first emission. Default true.
samplingLoopUpdate / FixedUpdate / Both.
keyCodesList<KeyCode> of keys to observe. Only these keys are logged.
holdModeOff (no held rows) / SampleWhileHeld (periodic rows while held) / StateTransitionsOnly (down + up only).
emitHeldEveryNSamplesWhen holdMode = SampleWhileHeld, emit a held row every Nth poll. Min 1, default 1.

Note: KeyCodeDataCapture is its own ScriptableObject — not a subclass of DataCaptureConfig. Same field names but separate inheritance.

Columns

Shared prefix

See Column conventions — The shared prefix.

Domain columns

ColumnTypeDescription
timeSecondsdoubleTime at which the input was polled. Typically matches ObservationTime from the shared prefix.
frameintThe frame the input was polled. Typically matches FrameNumber from the shared prefix.
isFixedTimeStepint1 if the poll happened during FixedUpdate, 0 for Update.
keyCodestringName of the KeyCode (e.g. Space, Return, A, Mouse0).
eventTypestringKeyDown, KeyUp, or KeyHeld.

Sample rows

...shared...,timeSeconds,frame,isFixedTimeStep,keyCode,eventType
...,12.345,745,0,Space,KeyDown
...,12.387,748,0,Space,KeyUp
...,12.390,749,0,Return,KeyDown

Join with other streams

Classic reaction-time pattern — pair a stimulus-onset event with the next key-down:

import pandas as pd
events = pd.read_csv("EventPoints.csv")
inputs = pd.read_csv("InputCapture.csv")

stim = events[events["Event_Description"] == "Stimulus flash"].sort_values("MonotonicExecutionTime")
keys = inputs[inputs["eventType"] == "KeyDown"].sort_values("MonotonicExecutionTime")

# For each stimulus, find the first KeyDown after it:
pairs = pd.merge_asof(
stim,
keys,
on="MonotonicExecutionTime",
direction="forward",
suffixes=("_stim", "_key"),
)
pairs["reaction_time_s"] = pairs["MonotonicExecutionTime_key"] - pairs["MonotonicExecutionTime_stim"]

See Reaction time recipe for the full pattern.

Gotchas

  • Only configured keys are logged. If a key isn't in the SO's keyCodes list, its presses are invisible to the CSV. Easy mistake if you added a key to the experience but forgot to add it to the capture config.
  • SampleWhileHeld can be chatty. With holdMode = SampleWhileHeld and emitHeldEveryNSamples = 1, a held key emits a row every poll — expect potentially very large files. Either raise emitHeldEveryNSamples or use StateTransitionsOnly for dwell analysis (compute duration from KeyDown / KeyUp pairs).
  • timeSeconds / frame duplicate shared columns. They mostly equal ObservationTime and FrameNumber from the shared prefix. Treat shared-prefix values as authoritative.
  • isFixedTimeStep is rarely 1. Input polling is typically in Update, not FixedUpdate. If you see isFixedTimeStep = 1, that's an unusual config — double-check.
  • Stamped at the frame of polling, not the hardware moment. If Unity missed an input on a slow frame, the reported time is the frame Unity observed it — which may be a frame or two late vs. actual finger-down. For sub-frame accuracy, couple this with display-offset correction — see Timing.

Analysis recipes