NOTE: Events are an advanced, mostly internal feature of the input system. It is not necessary to understand this feature to use the input system. Knowledge of the event system is mostly useful when wanting to support custom devices or when wanting to modify the behavior of existing devices.
Input Events
The input system is event-driven. All input is delivered as events and custom input can be generated by injecting events. Also, by listening in on the events flowing through the system, all source input can be observed.
Input events are a low-level mechanism. You will usually not need to deal with events yourself when all you want to do is receive input for your game. Events are stored in unmanaged memory buffers and not converted to C# heap objects. Wrapper APIs are provided but for more involved event manipulations, unsafe code is required.
Note that there is no routing mechanism. Events are delivered from the runtime straight to the input system where they are incorporated directly into device state.
Input events are represented by the InputEvent
struct. Each event has a set of properties common to all events:
Property | Description |
---|---|
type |
FourCC code that indicates what type of event it is. |
eventId |
Unique numeric of the event. |
time |
Timestamp of when the event was generated. |
deviceId |
ID of the device that the event is targeted at. |
sizeInBytes |
Total size of the event in bytes. |
The events received for a specific input device can be observed in the input debugger.
Types of Events
State Events
A state event contains input state for a device. These events are what gets new input into the system.
There are two types of state events:
StateEvent
('STAT'
)DeltaStateEvent
('DLTA'
)
StateEvent
contains a full snapshot of the entire state of a device in the format specific to the device. The stateFormat
field identifies the type of the data in the event. The raw data can be accessed using the state
pointer and is stateSizeInBytes
long.
DeltaStateEvent
are like StateEvent
, but a DeltaStateEvent
will only contain a partial snapshot of the state of a device. This is usually sent by the backend for devices requiring a large state record to reduce the amount of memory which needs to be updated if only some of the controls change their state. The raw data can be accessed using the deltaState
pointer and is deltaStateSizeInBytes
long. The data should be applied to the device's state at the offset at stateOffset
.
Device Events
Device events indicate a change that is relevant to a device as a whole. If you are interested in these events, it is usually more convenient to subscribe to the higher-level InputSystem.onDeviceChange
event rather then processing InputEvents
yourself.
DeviceRemoveEvent
('DREM'
)DeviceConfigurationEvent
('DCFG'
)
DeviceRemovedEvent
indicates that a device has been removed/disconnected. The device which has been removed can be queried from the common deviceId
field. This event does not have any additional data.
DeviceConfigurationEvent
indicates that the configuration of a device has changed. The meaning of this is device-specific. This may 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. The device which has been changed can be queried from the common deviceId
field. This event does not have any additional data.
Text Events
These events are sent by Keyboard devices to handle text input. If you are interested in these events, it is usually more convenient to subscribe to the higher-level callbacks on the Keyboard class rather then processing InputEvents
yourself.
TextEvent
('TEXT'
)IMECompositionEvent
('IMES'
)
Working With Events
Monitoring Events
If you want to do any monitoring or processing on incoming events yourself, subscribe to the InputSystem.onEvent
callback.
Reading State Events
State 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.
The easiest way to access state contained in a state event is to rely on the device the state is meant for. Any control can be asked to read its value from a given event rather than from its own internally stored state.
For example, the following code demonstrates how to read a value for Gamepad.leftStick
from a state event targeted at a Gamepad
.
InputSystem.onEvent +=
(eventPtr, device) =>
{
// Ignore anything that isn't a state event.
if (!eventPtr.IsA<StateEvent>() && !eventPtr.IsA<DeltaStateEvent>())
return;
var gamepad = device as Gamepad;
if (gamepad == null)
{
// Event isn't for a gamepad or device ID is no longer valid.
return;
}
var leftStickValue = gamepad.leftStick.ReadValueFromEvent(eventPtr);
};
Creating Events
New input events can be created and queued by anyone and against any existing device. Queueing an input event is thread-safe meaning that event generation can happen in background threads.
NOTE: Memory allocated to events coming from background threads is limited. If too many events are produced by background threads, queueing an event from a thread will block the thread until the main thread has flushed out the background event queue.
Note that queuing an event will not immediately consume the event. Processing of events happens on the next update (depending InputSettings.updateMode
, either manually triggered via InputSystem.Update
or triggered automatically as part of the player loop).
Sending State Events
The easiest way to create a state event is directly from the device.
// `StateEvent.From` creates a temporary buffer in unmanaged memory holding
// a state event large enough for the given device and containing a memory
// copy of the device's current state.
InputEventPtr eventPtr;
using (StateEvent.From(myDevice, out eventPtr))
{
((AxisControl) myDevice["myControl"]).WriteValueIntoEvent(0.5f, eventPtr);
InputSystem.QueueEvent(eventPtr);
}
Another way for sending state is to send events for individual controls.
// Send event to update leftStick on the gamepad.
InputSystem.QueueDeltaStateEvent(Gamepad.current.leftStick,
new Vector2(0.123f, 0.234f);
Note that delta state events only work for controls that are both byte-aligned and a multiple of 8 bits in size in memory. It is not supported to, for example, send a delta state event for a button control that is stored as a single bit.
Capturing Events
You can use the InputEventTrace
class to record input events for later processing:
var trace = new InputEventTrace(); // Can also give device ID to only
// trace events for a specific device.
trace.Enable();
//... run stuff
var current = new InputEventPtr();
while (trace.GetNextEvent(ref current))
{
Debug.Log("Got some event: " + current);
}
// Also supports IEnumerable.
foreach (var eventPtr in trace)
Debug.Log("Got some event: " + eventPtr);
// Trace consumes unmanaged resources. Make sure to dispose.
trace.Dispose();