# 4. UI Input and XR Input
Part of [[Input System]]. Previous: [[3. Runtime Rebinding]]. Next: [[5. Worked Example Remappable Player]].
Desktop UI companions: [[UI Toolkit and Diegetic UI]]. VR UI: [[8.UI Toolkit for VR]]. Quest device/hands detail lives in [[Meta Quest 2]] and [[6.Quest 2 Input Hands and Passthrough]] — this part only shows how those actions sit on the Input System.
> [!warning] Verification status
> `InputSystemUIInputModule`, tracked PassThrough requirement, and `TrackedDeviceRaycaster` come from Input System **1.20.1** UI docs. XR Interaction Toolkit `XRUIInputModule` notes summarize XRI package docs and were **not** validated against a specific XRI version in-editor. Quest binding paths are **unverified** device strings — prefer the maps from your Quest tutorial set. Scripts were **not** compiled in Unity.
## 4.1 EventSystem + Input System UI Input Module
Docs: [Introduction to the UI Input Module](https://docs.unity3d.com/Packages/
[email protected]/manual/introduction-ui-input-module.html), [Supported input types](https://docs.unity3d.com/Packages/
[email protected]/manual/supported-ui-input-types.html).
For **uGUI** (and UI Toolkit panels that still go through EventSystem):
1. Scene needs an **Event System**.
2. Remove **Standalone Input Module** (legacy).
3. Add **Input System UI Input Module** (`InputSystemUIInputModule`).
4. Point its actions at your asset's **UI** map (or leave project-wide defaults).
Default UI actions usually include Navigate, Submit, Cancel, Point, Click, Scroll Wheel, Middle/Right Click, and tracked device Pose fields.
When a component instance exists in the scene, **its** assignments win over project-wide UI settings, and it enables the UI map alongside the module's default map.
```csharp
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem.UI;
public static class UiInputBootstrap
{
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
static void EnsureModule()
{
var es = EventSystem.current;
if (es == null)
return;
if (es.GetComponent<InputSystemUIInputModule>() != null)
return;
// Illustrative only — prefer configuring in the Editor.
var legacy = es.GetComponent<StandaloneInputModule>();
if (legacy != null)
Object.Destroy(legacy);
es.gameObject.AddComponent<InputSystemUIInputModule>();
}
}
```
## 4.2 UI Toolkit note
On modern Unity 6, UI Toolkit can receive pointer events without always needing the same uGUI path you used in 2021-era projects, but **gamepad/keyboard navigation** and shared pause menus still commonly use EventSystem + `InputSystemUIInputModule`. Diegetic world-space panels are covered in [[UI Toolkit and Diegetic UI]]; VR world-space Toolkit panels are in [[8.UI Toolkit for VR]]. Do not run two competing input modules on one EventSystem.
## 4.3 Pausing gameplay while UI is open
```csharp
using UnityEngine;
using UnityEngine.InputSystem;
public sealed class PauseInputGate : MonoBehaviour
{
[SerializeField] InputActionAsset actions;
InputActionMap _player;
InputActionMap _ui;
void Awake()
{
_player = actions.FindActionMap("Player");
_ui = actions.FindActionMap("UI");
}
public void SetPaused(bool paused)
{
if (paused)
{
_player.Disable();
_ui.Enable();
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
}
else
{
_ui.Disable();
_player.Enable();
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
}
}
```
During **rebinding**, keep UI Submit/Cancel enabled (or a dedicated Menus map) so Esc can cancel; disable only the Player actions being rebound.
## 4.4 Tracked-device (XR) UI with the Input System module
Docs: [Tracked input UI support](https://docs.unity3d.com/Packages/
[email protected]/manual/supported-ui-input-types-tracked.html).
Tracked controllers feed **world pose** into UI via:
- Actions for `trackedDevicePosition` and `trackedDeviceOrientation` — type **PassThrough** (mandatory so both hands relay).
- **`TrackedDeviceRaycaster`** on the Canvas GameObject (can sit beside `GraphicRaycaster`).
- Ordinary click actions (`leftClick` / Click) for press.
Without PassThrough, conflict resolution will drop one controller's stream and UI rays feel "possessed" by a single hand.
## 4.5 XR Interaction Toolkit path (Quest projects)
For Meta Quest work you already document under [[Meta Quest 2]], XRI expects **`XRUIInputModule`** on the EventSystem and tells you to **remove** Standalone / Input System UI Input Module so only one module runs. Prefer the **XRI Default Input Actions** / **XRI Default XR UI Input Module** preset from XRI Starter Assets when available.
Relationship:
| Stack | UI module | When |
|---|---|---|
| Desktop / non-XRI | `InputSystemUIInputModule` | Flat games, editor tools, non-XR UI Toolkit |
| XRI / Quest interactors | `XRUIInputModule` | Near-Far / Ray / Poke UI |
| Input System tracked rays without XRI | `InputSystemUIInputModule` + `TrackedDeviceRaycaster` | Custom XR without XRI |
Do **not** duplicate the hands, passthrough, or OpenXR setup here — use [[6.Quest 2 Input Hands and Passthrough]]. From this tutorial you only need:
1. Separate **Player** vs **UI** vs **XRI** maps (or schemes).
2. PassThrough on tracked pose actions.
3. One EventSystem input module appropriate to the stack.
4. Awareness that Quest builds for Marshall's sideload fleet target **Unity 6.3 LTS (6000.3.27f2+)**, while this general Input System deep dive stays on **6000.5+**.
## 4.6 Minimal XR controller action sketch (desktop mock or custom XR)
```csharp
using UnityEngine;
using UnityEngine.InputSystem;
/// <summary>
/// Reads a PassThrough Vector3 pose action. Bindings come from your XR device layout.
/// Prefer XRI's action assets in Quest projects instead of hand-rolling this.
/// </summary>
public sealed class XrPoseDebug : MonoBehaviour
{
[SerializeField] InputActionReference trackedPosition; // PassThrough, Vector3
[SerializeField] InputActionReference trackedRotation; // PassThrough, Quaternion
void OnEnable()
{
trackedPosition.action.Enable();
trackedRotation.action.Enable();
trackedPosition.action.performed += OnPos;
}
void OnDisable()
{
trackedPosition.action.performed -= OnPos;
trackedPosition.action.Disable();
trackedRotation.action.Disable();
}
void OnPos(InputAction.CallbackContext ctx)
{
Vector3 p = ctx.ReadValue<Vector3>();
Debug.Log(