Game.Input.DeviceListener
Assembly: Assembly-CSharp
Namespace: Game.Input
Type: public class
Base: IInputStateChangeMonitor, IDisposable
Summary:
DeviceListener watches a specific Unity InputSystem InputDevice for button presses and raises a UnityEvent when the device is activated. It registers itself as an InputState change monitor on the device's valid controls (currently only ButtonControl), buffers activation state, and exposes a UnityEvent
Fields
-
private List<InputControl> m_Controls
Holds the filtered list of controls from the target device that the listener monitors. Only controls that pass ValidateControl (button controls) are added. -
private bool m_Listening
Tracks whether the listener is currently registered with the InputState change system. -
private float m_RequiredDelta
A threshold value supplied in the constructor. In the provided implementation this field is stored but not used — likely intended for future use to require a minimum change magnitude before activation. -
private float m_Delta
Internal timer/delta used by Tick() to decay over time. Its current behavior is a simple timed decay; specific uses beyond that are not present in this class. -
private bool m_Activated
Set when a monitored control is detected as pressed in NotifyControlStateChanged. Tick() consumes this flag and triggers the event. -
public DeviceEvent EventDeviceActivated
UnityEventinstance used for subscribers to be notified when the device is activated. -
(Nested Type)
public class DeviceEvent : UnityEvent<InputDevice>
A UnityEvent specialization that carries the activated InputDevice to listeners.
Properties
public InputDevice device { get; private set; }
The InputDevice instance this listener monitors. The setter is private; the device is provided at construction.
Constructors
public DeviceListener(InputDevice device, float requiredDelta)
Creates a DeviceListener for the given device. It:- Initializes the EventDeviceActivated UnityEvent.
- Stores the device and requiredDelta.
- Builds the m_Controls list by iterating device.allControls and adding controls that ValidateControl accepts (currently ButtonControl instances).
Methods
-
public void Tick()
Should be called regularly (e.g., once per frame). It decays m_Delta over Time.deltaTime and, if m_Activated was set by the change monitor, resets m_Activated and invokes EventDeviceActivated with the monitored device. This decouples InputSystem notifications from when subscribers are notified (ensures invocation happens on the Tick caller's context). -
public void StartListening()
Begin monitoring the device's filtered controls. If already listening, this is a no-op. Resets activation state and m_Delta and registers this instance as a change monitor for each control via InputState.AddChangeMonitor(control, this, -1L). -
public void StopListening()
Stops monitoring. If not listening, this is a no-op. Clears activation state and m_Delta and removes the change monitors via InputState.RemoveChangeMonitor(control, this, -1L). -
private bool ValidateControl(InputControl control)
Determines whether a control should be monitored. Current implementation returns true only for ButtonControl (so only button-type controls are tracked). -
public void NotifyControlStateChanged(InputControl control, double time, InputEventPtr eventPtr, long monitorIndex)
IInputStateChangeMonitor callback invoked by the Input System when the control state changes. If the control is a ButtonControl and wasPressedThisFrame is true, sets m_Activated = true. Actual event invocation is deferred until Tick(). -
public void NotifyTimerExpired(InputControl control, double time, long monitorIndex, int timerIndex)
IInputStateChangeMonitor callback for timer expiration. Currently empty (no behavior implemented). -
public void Dispose()
Implements IDisposable. Calls StopListening() to remove change monitors and clear state.
Notes and caveats: - m_RequiredDelta is stored but unused in the current implementation — if your mod expects threshold filtering, you'll need to extend the class to make use of it. - NotifyControlStateChanged uses wasPressedThisFrame which requires the Unity Input System to be properly updated and may rely on being called on the main thread / during normal input processing. - Tick() must be called by client code (for example, from a MonoBehaviour.Update) so that EventDeviceActivated gets invoked on the main thread and subscribers are notified.
Usage Example
// Example MonoBehaviour that uses DeviceListener to watch an InputDevice
using UnityEngine;
using UnityEngine.InputSystem;
using Game.Input;
public class DeviceWatcher : MonoBehaviour
{
private DeviceListener _listener;
void Start()
{
// Example: pick the first Gamepad if available
InputDevice device = Gamepad.current;
if (device == null)
{
Debug.LogWarning("No gamepad found.");
return;
}
_listener = new DeviceListener(device, 0.1f);
_listener.EventDeviceActivated.AddListener(OnDeviceActivated);
_listener.StartListening();
}
void Update()
{
// Must call Tick every frame so the listener can invoke the UnityEvent on the main thread
_listener?.Tick();
}
private void OnDeviceActivated(InputDevice device)
{
Debug.Log($"Device activated: {device.displayName}");
// Handle activation (e.g., assign device, show UI, etc.)
}
void OnDestroy()
{
// Clean up and remove monitors
if (_listener != null)
{
_listener.EventDeviceActivated.RemoveListener(OnDeviceActivated);
_listener.Dispose();
_listener = null;
}
}
}
{{ Additional notes: This class is a thin adapter between the Unity Input System's low-level change monitors and UnityEvent-based callbacks. If you need to monitor axes or analog controls, extend ValidateControl and NotifyControlStateChanged logic accordingly. If using multiple DeviceListener instances, ensure proper StartListening/StopListening lifecycle management to avoid leftover monitors. }}