A game about forced loneliness, made by TACStudios
1--- 2uid: input-system-events 3--- 4# Input events 5 6* [Types of events](#types-of-events) 7 * [State events](#state-events) 8 * [Device events](#device-events) 9 * [Text events](#text-events) 10* [Working with events](#working-with-events) 11 * [Listening to events](#listening-to-events) 12 * [Reading state events](#reading-state-events) 13 * [Creating events](#creating-events) 14 * [Capturing events](#capturing-events) 15* [Processing events](#processing-events) 16 * [Merging of events](#merging-of-events) 17 18The Input System is event-driven. All input is delivered as events, and you can generate custom input by injecting events. You can also observe all source input by listening in on the events flowing through the system. 19 20>__Note__: Events are an advanced, mostly internal feature of the Input System. Knowledge of the event system is mostly useful if you want to support custom Devices, or change the behavior of existing Devices. 21 22Input events are a low-level mechanism. Usually, you don't need to deal with events if all you want to do is receive input for your app. Events are stored in unmanaged memory buffers and not converted to C# heap objects. The Input System provides wrapper APIs, but unsafe code is required for more involved event manipulations. 23 24Note that there are no routing mechanism. The runtime delivers events straight to the Input System, which then incorporates them directly into the Device state. 25 26Input events are represented by the [`InputEvent`](../api/UnityEngine.InputSystem.LowLevel.InputEvent.html) struct. Each event has a set of common properties: 27 28|Property|Description| 29|--------|-----------| 30|[`type`](../api/UnityEngine.InputSystem.LowLevel.InputEvent.html#UnityEngine_InputSystem_LowLevel_InputEvent_type)|[`FourCC`](../api/UnityEngine.InputSystem.Utilities.FourCC.html) code that indicates what type of event it is.| 31|[`eventId`](../api/UnityEngine.InputSystem.LowLevel.InputEvent.html#UnityEngine_InputSystem_LowLevel_InputEvent_eventId)|Unique numeric ID of the event.| 32|[`time`](../api/UnityEngine.InputSystem.LowLevel.InputEvent.html#UnityEngine_InputSystem_LowLevel_InputEvent_time)|Timestamp of when the event was generated. This is on the same timeline as [`Time.realtimeSinceStartup`](https://docs.unity3d.com/ScriptReference/Time-realtimeSinceStartup.html).| 33|[`deviceId`](../api/UnityEngine.InputSystem.LowLevel.InputEvent.html#UnityEngine_InputSystem_LowLevel_InputEvent_deviceId)|ID of the Device that the event targets.| 34|[`sizeInBytes`](../api/UnityEngine.InputSystem.LowLevel.InputEvent.html#UnityEngine_InputSystem_LowLevel_InputEvent_sizeInBytes)|Total size of the event in bytes.| 35 36You can observe the events received for a specific input device in the [input debugger](Debugging.md#debugging-devices). 37 38## Types of events 39 40### State events 41 42A state event contains the input state for a Device. The Input System uses these events to feed new input to Devices. 43 44There are two types of state events: 45 46* [`StateEvent`](../api/UnityEngine.InputSystem.LowLevel.StateEvent.html) (`'STAT'`) 47* [`DeltaStateEvent`](../api/UnityEngine.InputSystem.LowLevel.StateEvent.html) (`'DLTA'`) 48 49[`StateEvent`](../api/UnityEngine.InputSystem.LowLevel.StateEvent.html) contains a full snapshot of the entire state of a Device in the format specific to that Device. The [`stateFormat`](../api/UnityEngine.InputSystem.LowLevel.StateEvent.html#UnityEngine_InputSystem_LowLevel_StateEvent_stateFormat) field identifies the type of the data in the event. You can access the raw data using the [`state`](../api/UnityEngine.InputSystem.LowLevel.StateEvent.html#UnityEngine_InputSystem_LowLevel_StateEvent_state) pointer and [`stateSizeInBytes`](../api/UnityEngine.InputSystem.LowLevel.StateEvent.html#UnityEngine_InputSystem_LowLevel_StateEvent_stateSizeInBytes). 50 51A [`DeltaStateEvent`](../api/UnityEngine.InputSystem.LowLevel.DeltaStateEvent.html) is like a [`StateEvent`](../api/UnityEngine.InputSystem.LowLevel.StateEvent.html), but only contains a partial snapshot of the state of a Device. The Input System usually sends this for Devices that require a large state record, to reduce the amount of memory it needs to update if only some of the Controls change their state. To access the raw data, you can use the [`deltaState`](../api/UnityEngine.InputSystem.LowLevel.DeltaStateEvent.html#UnityEngine_InputSystem_LowLevel_DeltaStateEvent_deltaState) pointer and [`deltaStateSizeInBytes`](../api/UnityEngine.InputSystem.LowLevel.DeltaStateEvent.html#UnityEngine_InputSystem_LowLevel_DeltaStateEvent_deltaStateSizeInBytes). The Input System should apply the data to the Device's state at the offset defined by [`stateOffset`](../api/UnityEngine.InputSystem.LowLevel.DeltaStateEvent.html#UnityEngine_InputSystem_LowLevel_DeltaStateEvent_stateOffset). 52 53### Device events 54 55Device events indicate a change that is relevant to a Device as a whole. If you're interested in these events, it is usually more convenient to subscribe to the higher-level [`InputSystem.onDeviceChange`](../api/UnityEngine.InputSystem.InputSystem.html#UnityEngine_InputSystem_InputSystem_onDeviceChange) event rather then processing [`InputEvents`](../api/UnityEngine.InputSystem.LowLevel.InputEvent.html) yourself. 56 57There are three types of Device events: 58 59* [`DeviceRemoveEvent`](../api/UnityEngine.InputSystem.LowLevel.DeviceRemoveEvent.html) (`'DREM'`) 60* [`DeviceConfigurationEvent`](../api/UnityEngine.InputSystem.LowLevel.DeviceConfigurationEvent.html) (`'DCFG'`) 61* [`DeviceResetEvent`](../api/UnityEngine.InputSystem.LowLevel.DeviceResetEvent.html) (`'DRST'`) 62 63`DeviceRemovedEvent` indicates that a Device has been removed or disconnected. To query the device that has been removed, you can use the common [`deviceId`](../api/UnityEngine.InputSystem.LowLevel.InputEvent.html#UnityEngine_InputSystem_LowLevel_InputEvent_deviceId) field. This event doesn't have any additional data. 64 65`DeviceConfigurationEvent` indicates that the configuration of a Device has changed. The meaning of this is Device-specific. This might signal, for example, that the layout used by the keyboard has changed or that, on a console, a gamepad has changed which player ID(s) it is assigned to. You can query the changed device from the common [`deviceId`](../api/UnityEngine.InputSystem.LowLevel.InputEvent.html#UnityEngine_InputSystem_LowLevel_InputEvent_deviceId) field. This event doesn't have any additional data. 66 67`DeviceResetEvent` indicates that a device should get reset. This will trigger [`InputSystem.ResetDevice`](../api/UnityEngine.InputSystem.InputSystem.html#UnityEngine_InputSystem_InputSystem_ResetDevice_UnityEngine_InputSystem_InputDevice_System_Boolean_) to be called on the Device. 68 69### Text events 70 71[Keyboard](Keyboard.md) devices send these events to handle text input. If you're interested in these events, it's usually more convenient to subscribe to the higher-level [callbacks on the Keyboard class](Keyboard.md#text-input) rather than processing [`InputEvents`](../api/UnityEngine.InputSystem.LowLevel.InputEvent.html) yourself. 72 73There are two types of text events: 74 75* [`TextEvent`](../api/UnityEngine.InputSystem.LowLevel.TextEvent.html) (`'TEXT'`) 76* [`IMECompositionEvent`](../api/UnityEngine.InputSystem.LowLevel.IMECompositionEvent.html) (`'IMES'`) 77 78## Working with events 79 80### Listening to events 81 82If you want to do any monitoring or processing on incoming events yourself, subscribe to the [`InputSystem.onEvent`](../api/UnityEngine.InputSystem.InputSystem.html#UnityEngine_InputSystem_InputSystem_onEvent) callback. 83 84```CSharp 85InputSystem.onEvent += 86 (eventPtr, device) => 87 { 88 Debug.Log($"Received event for {device}"); 89 }; 90``` 91 92An [`IObservable`](https://docs.microsoft.com/en-us/dotnet/api/system.iobservable-1) interface is provided to more conveniently process events. 93 94```CSharp 95// Wait for first button press on a gamepad. 96InputSystem.onEvent 97 .ForDevice<Gamepad>() 98 .Where(e => e.HasButtonPress()) 99 .CallOnce(ctrl => Debug.Log($"Button {ctrl} pressed")); 100``` 101 102To enumerate the controls that have value changes in an event, you can use [`InputControlExtensions.EnumerateChangedControls`](../api/UnityEngine.InputSystem.InputControlExtensions.html#UnityEngine_InputSystem_InputControlExtensions_EnumerateChangedControls_UnityEngine_InputSystem_LowLevel_InputEventPtr_UnityEngine_InputSystem_InputDevice_System_Single_). 103 104```CSharp 105InputSystem.onEvent 106 .Call(eventPtr => 107 { 108 foreach (var control in eventPtr.EnumerateChangedControls()) 109 Debug.Log($"Control {control} changed value to {control.ReadValueFromEventAsObject(eventPtr)}"); 110 }; 111``` 112 113This is significantly more efficient than manually iterating over [`InputDevice.allControls`](../api/UnityEngine.InputSystem.InputDevice.html#UnityEngine_InputSystem_InputDevice_allControls) and reading out the value of each control from the event. 114 115### Reading state events 116 117State events contain raw memory snapshots for Devices. As such, interpreting the data in the event requires knowledge about where and how individual state is stored for a given Device. 118 119The easiest way to access state contained in a state event is to rely on the Device that the state is meant for. You can ask any Control to read its value from a given event rather than from its own internally stored state. 120 121For example, the following code demonstrates how to read a value for [`Gamepad.leftStick`](../api/UnityEngine.InputSystem.Gamepad.html#UnityEngine_InputSystem_Gamepad_leftStick) from a state event targeted at a [`Gamepad`](../api/UnityEngine.InputSystem.Gamepad.html). 122 123```CSharp 124InputSystem.onEvent += 125 (eventPtr, device) => 126 { 127 // Ignore anything that isn't a state event. 128 if (!eventPtr.IsA<StateEvent>() && !eventPtr.IsA<DeltaStateEvent>()) 129 return; 130 131 var gamepad = device as Gamepad; 132 if (gamepad == null) 133 { 134 // Event isn't for a gamepad or device ID is no longer valid. 135 return; 136 } 137 138 var leftStickValue = gamepad.leftStick.ReadValueFromEvent(eventPtr); 139 }; 140``` 141 142### Creating events 143 144Anyone can create and queue new input events against any existing Device. Queueing an input event is thread-safe, which means that event generation can happen in background threads. 145 146>__Note__: Unity allocates limited memory to events that come from background threads. If background threads produce too many events, queueing an event from a thread blocks the thread until the main thread flushes out the background event queue. 147 148Note that queuing an event doesn't immediately consume the event. Event processing happens on the next update (depending on [`InputSettings.updateMode`](Settings.md#update-mode), it is triggered either manually via [`InputSystem.Update`](../api/UnityEngine.InputSystem.InputSystem.html#UnityEngine_InputSystem_InputSystem_Update), or automatically as part of the Player loop). 149 150#### Sending state events 151 152For Devices that have a corresponding "state struct" describing the state of the device, the easiest way of sending input to the Device is to simply queue instances of those structs: 153 154```CSharp 155// Mouse. 156InputSystem.QueueStateEvent(Mouse.current, new MouseState { position = new Vector2(123, 234) }); 157 158// Keyboard. 159InputSystem.QueueStateEvent(Keyboard.current, new KeyboardState(Key.LeftCtrl, Key.A)); 160``` 161 162`Touchscreen` is somewhat special in that it expects its input to be in [`TouchState`](../api/UnityEngine.InputSystem.LowLevel.TouchState.html) format. 163 164```CSharp 165// Start touch. 166InputSystem.QueueStateEvent(Touchscreen.current, 167 new TouchState { touchId = 1, phase = TouchPhase.Began, position = new Vector2(123, 234) }); 168 169// Move touch. 170InputSystem.QueueStateEvent(Touchscreen.current, 171 new TouchState { touchId = 1, phase = TouchPhase.Moved, position = new Vector2(234, 345) }); 172 173// End touch. 174InputSystem.QueueStateEvent(Touchscreen.current, 175 new TouchState { touchId = 1, phase = TouchPhase.Ended, position = new Vector2(123, 234) }); 176``` 177 178>__IMPORTANT:__ [Touch IDs](../api/UnityEngine.InputSystem.Controls.TouchControl.html#UnityEngine_InputSystem_Controls_TouchControl_touchId) cannot be 0! A valid touch must have a non-zero touch ID. Concurrent touches must each have a unique ID. After a touch has ended, its ID can be reused &ndash; although it is recommended to not do so. 179 180If the exact format of the state used by a given Device is not known, the easiest way to send input to it is to simply create a [`StateEvent`](../api/UnityEngine.InputSystem.LowLevel.StateEvent.html) from the Device itself: 181 182```CSharp 183// `StateEvent.From` creates a temporary buffer in unmanaged memory that holds 184// a state event large enough for the given device and contains a memory 185// copy of the device's current state. 186InputEventPtr eventPtr; 187using (StateEvent.From(myDevice, out eventPtr)) 188{ 189 ((AxisControl) myDevice["myControl"]).WriteValueIntoEvent(0.5f, eventPtr); 190 InputSystem.QueueEvent(eventPtr); 191} 192``` 193 194Alternatively, you can send events for individual Controls. 195 196```CSharp 197// Send event to update leftStick on the gamepad. 198InputSystem.QueueDeltaStateEvent(Gamepad.current.leftStick, 199 new Vector2(0.123f, 0.234f); 200``` 201 202Note that delta state events only work for Controls that are both byte-aligned and a multiple of 8 bits in size in memory. You can't send a delta state event for a button Control that is stored as a single bit, for example. 203 204### Capturing Events 205 206>NOTE: To download a sample project which contains a reusable MonoBehaviour called `InputRecorder`, which can capture and replay input from arbitrary devices, open the Package Manager, select the Input System Package, and choose the sample project "Input Recorder" to download. 207 208You can use the [`InputEventTrace`](../api/UnityEngine.InputSystem.LowLevel.InputEventTrace.html) class to record input events for later processing: 209 210```CSharp 211var trace = new InputEventTrace(); // Can also give device ID to only 212 // trace events for a specific device. 213 214trace.Enable(); 215 216//... run stuff 217 218var current = new InputEventPtr(); 219while (trace.GetNextEvent(ref current)) 220{ 221 Debug.Log("Got some event: " + current); 222} 223 224// Also supports IEnumerable. 225foreach (var eventPtr in trace) 226 Debug.Log("Got some event: " + eventPtr); 227 228// Trace consumes unmanaged resources. Make sure to dispose. 229trace.Dispose(); 230``` 231 232Dispose event traces after use, so that they do not leak memory on the unmanaged (C++) memory heap. 233 234You can also write event traces out to files/streams, load them back in, and replay recorded streams. 235 236```CSharp 237// Set up a trace with such that it automatically grows in size as needed. 238var trace = new InputEventTrace(growBuffer: true); 239trace.Enable(); 240 241// ... capture some input ... 242 243// Write trace to file. 244trace.WriteTo("mytrace.inputtrace."); 245 246// Load trace from same file. 247var loadedTrace = InputEventTrace.LoadFrom("mytrace.inputtrace"); 248``` 249 250You can replay captured traces directly from [`InputEventTrace`](../api/UnityEngine.InputSystem.LowLevel.InputEventTrace.html) instances using the [`Replay`](../api/UnityEngine.InputSystem.LowLevel.InputEventTrace.html#UnityEngine_InputSystem_LowLevel_InputEventTrace_Replay_) method. 251 252```CSharp 253// The Replay method returns a ReplayController that can be used to 254// configure and control playback. 255var controller = trace.Replay(); 256 257// For example, to not replay the events as is but rather create new devices and send 258// the events to them, call WithAllDevicesMappedToNewInstances. 259controller.WithAllDevicessMappedToNewInstances(); 260 261// Replay all frames one by one. 262controller.PlayAllFramesOnyByOne(); 263 264// Replay events in a way that tries to simulate original event timing. 265controller.PlayAllEventsAccordingToTimestamps(); 266``` 267 268## Processing events 269 270[Events](../api/UnityEngine.InputSystem.LowLevel.InputEvent.html) are collected on a queue by the Unity runtime. This queue is regularly flushed out and the events on it processed. Events can be added to the queue manually by calling [`InputSystem.QueueEvent`](../api/UnityEngine.InputSystem.InputSystem.html#UnityEngine_InputSystem_InputSystem_QueueEvent_UnityEngine_InputSystem_LowLevel_InputEventPtr_). 271 272Each time input is processed, [`InputSystem.Update`](../api/UnityEngine.InputSystem.InputSystem.html#UnityEngine_InputSystem_InputSystem_Update_) is called implicitly by the Unity runtime. 273 274The interval at which this happens is determined by the ["Update Mode"](Settings.md#update-mode) configured in the settings. By default, input is processed in each frame __before__ <c>MonoBehaviour.Update</c> methods are called. If the setting is changed to process input in fixed updates, then this changes to input being processed each time before <c>MonoBehaviour.FixedUpdate</c> methods are called. 275 276Normally, when input is processed, __all__ outstanding input events on the queue will be consumed. There are two exceptions to this, however. 277 278When using [`UpdateMode.ProcessEventsInFixedUpdate`](../api/UnityEngine.InputSystem.InputSettings.UpdateMode.html#UnityEngine_InputSystem_InputSettings_UpdateMode_ProcessEventsInFixedUpdate), the Input System attempts to associate events with the timeslice of the corresponding <c>FixedUpdate</c>. This is based on the [timestamps](../api/UnityEngine.InputSystem.LowLevel.InputEvent.html#UnityEngine_InputSystem_LowLevel_InputEvent_time) of the events and a "best effort" at calculating the corresponding timeslice of the current <c>FixedUpdated</c>. 279 280The other exception are [`BeforeRender`](../api/UnityEngine.InputSystem.LowLevel.InputUpdateType.html#UnityEngine_InputSystem_LowLevel_InputUpdateType_BeforeRender) updates. These updates are run after fixed or dynamic updates but before rendering and used used exclusively to update devices such as VR headsets that need the most up-to-date tracking data. Other input is not consumed from such updates and these updates are only enabled if such devices are actually present. `BeforeRender` updates are not considered separate frames as far as input is concerned. 281 282>__Note__: Manually calling [`InputSystem.Update`](../api/UnityEngine.InputSystem.InputSystem.html#UnityEngine_InputSystem_InputSystem_Update_) is strongly advised against except within tests employing [`InputTestFixture`](../api/UnityEngine.InputSystem.InputTestFixture.html) or when explicitly setting the system to [manual update mode](../api/UnityEngine.InputSystem.InputSettings.UpdateMode.html#UnityEngine_InputSystem_InputSettings_UpdateMode_ProcessEventsManually). 283 284Methods such as [`InputAction.WasPerformedThisFrame`](../api/UnityEngine.InputSystem.InputAction.html#UnityEngine_InputSystem_InputAction_WasPerformedThisFrame) and [`InputAction.WasPerformedThisFrame`](../api/UnityEngine.InputSystem.InputAction.html#UnityEngine_InputSystem_InputAction_WasPerformedThisFrame) operate implicitly based on the [`InputSystem.Update`] cadence described above. Meaning, that they refer to the state as per the __last__ fixed/dynamic/manual update happened. 285 286You can query the [current/last update type](../api/UnityEngine.InputSystem.LowLevel.InputState.html#UnityEngine_InputSystem_LowLevel_InputState_currentUpdateType) and [count](../api/UnityEngine.InputSystem.LowLevel.InputState.html#UnityEngine_InputSystem_LowLevel_InputState_updateCount) from [`InputState`](../api/UnityEngine.InputSystem.LowLevel.InputState.html). 287 288### Merging of events 289 290Input system uses event mering to reduce amount of events required to be processed. 291This greatly improves performance when working with high refresh rate devices like 8000 Hz mice, touchscreens and others. 292 293For example let's take a stream of 7 mouse events coming in the same update: 294 295``` 296 297Mouse Mouse Mouse Mouse Mouse Mouse Mouse 298Event no1 Event no2 Event no3 Event no4 Event no5 Event no6 Event no7 299Time 1 Time 2 Time 3 Time 4 Time 5 Time 6 Time 7 300Pos(10,20) Pos(12,21) Pos(13,23) Pos(14,24) Pos(16,25) Pos(17,27) Pos(18,28) 301Delta(1,1) Delta(2,1) Delta(1,2) Delta(1,1) Delta(2,1) Delta(1,2) Delta(1,1) 302BtnLeft(0) BtnLeft(0) BtnLeft(0) BtnLeft(1) BtnLeft(1) BtnLeft(1) BtnLeft(1) 303``` 304 305To reduce workload we can skip events that are not encoding button state changes: 306 307``` 308 Mouse Mouse Mouse 309 Time 3 Time 4 Time 7 310 Event no3 Event no4 Event no7 311 Pos(13,23) Pos(14,24) Pos(18,28) 312 Delta(3,3) Delta(1,1) Delta(4,4) 313 BtnLeft(0) BtnLeft(1) BtnLeft(1) 314``` 315 316In that case we combine no1, no2, no3 together into no3 and accumulate the delta, 317then we keep no4 because it stores the transition from button unpressed to button pressed, 318and it's important to keep the exact timestamp of such transition. 319Later we combine no5, no6, no7 together into no7 because it is the last event in the update. 320 321Currently this approach is implemented for: 322- `FastMouse`, combines events unless `buttons` or `clickCount` differ in `MouseState`. 323- `Touchscreen`, combines events unless `touchId`, `phaseId` or `flags` differ in `TouchState`. 324 325You can disable merging of events by: 326``` 327InputSystem.settings.disableRedundantEventsMerging = true; 328```