Develop

Hands, Controllers, and Gaze

Updated: Sep 8, 2026
Gaze mode changes how the rig routes hand and controller input. This page explains the exclusivity model, the asymmetry between hands and controllers, and what remains available while gaze mode is active.

What Happens to Hand and Controller Input in Gaze Mode?

In Interaction SDK for Unreal, gaze mode is exclusive with ray input. When gaze mode turns on, sibling ray interaction rigs turn off; when gaze mode turns off, they turn back on. There is no additive arbitration between a gaze pointer and a hand or controller ray.
The mechanism is a conditional gate wired onto each ray interaction rig. UIsdkGazeRigComponent::WireHandInteractorGazeGate() builds a UIsdkConditionalGroupNone (a NOR) around the internal gaze mode boolean. That gate resolves true exactly when gaze mode is off, and it is added to each ray rig’s enabled conditional group, which is a logical AND. A ray interactor is therefore enabled only while gaze mode is off.
Component chain from the gaze mode boolean through a UIsdkConditionalGroupNone NOR gate into each ray rig's AND group.
WireHandInteractorGazeGate builds the NOR gate and adds it as a term to every ray interaction rig on the owner.
This is the Unreal model. Other engines arbitrate gaze against ray inputs additively, running a gaze interactable alongside the hand ray interactables. In Unreal, gaze mode turns hand and controller rays off rather than competing with them.

How Controllers Behave Differently from Hands

Hand rigs and controller rigs behave asymmetrically in gaze mode.
The gate that disables ray interactors collects every ray interaction rig on the owner. WireHandInteractorGazeGate() calls Owner->GetComponents() for UIsdkRayInteractionRigComponent with no filter on hand versus controller. Because UIsdkControllerRigComponent derives from UIsdkRigComponent and hosts ray interactors the same way a hand rig does, the controller’s ray interactor is gated off in gaze mode alongside the hand ray.
Components panel of the sample pawn with two controller rig components and the gaze rig alongside the hand rigs.
Gaze selection actuation, however, is wired only for hand rigs. SubscribeHandSelectDelegates() iterates the owner’s UIsdkHandRigComponent collection to bind hand select and unselect events that drive gaze selection. UIsdkControllerRigComponent is not a UIsdkHandRigComponent, so no controller input path binds into HandleHandSelect or HandleHandUnselect. A user holding a controller in gaze mode therefore has the controller’s ray disabled and no path to actuate a gaze selection from the controller.
Play-in-Editor scene with gaze mode off and with gaze mode on, where the controller's ray affordances are gone.
Decision flow comparing a hand rig and a controller rig once gaze mode turns on.
One unfiltered query gates the ray interactor off on both rigs, while SubscribeHandSelectDelegates binds actuation only on the hand rig.

What Stays Available in Gaze Mode

Not everything shuts off when gaze mode turns on. ApplyGazeModeToGrabbers() deliberately preserves near hand grab. It marks RayGrab as allowed on the grabbers and DistanceGrab as disallowed, and the source notes that direct or hand grab is left at its default so near hand grab stays available.
Gaze mode also injects a different ray grab detector on each grabber. That mechanism is described in depth in Gaze Grab Interactions.
Two-hand arbitration keeps gaze selection unambiguous. The component tracks the currently selecting hand in ActuatingHand, a TWeakObjectPtr<UIsdkRigComponent>. The first hand to begin a selection owns the interaction, and the other hand’s begin event is ignored until the owning hand releases. A single region interactor, UIsdkRegionInteractionRigComponent, is shared by both hands.
State diagram of two-hand gaze arbitration, from ActuatingHand null to the owning hand and back on release.
While one hand owns the selection, the other hand’s begin event is ignored and causes no state change.

Toggling Gaze Mode

Three entry points toggle gaze mode. All resolve to the same underlying state, and SetGazeMode() applies on the next tick during reconciliation.
Entry PointSignatureAvailability
Rig method
UIsdkGazeRigComponent::SetGazeMode(bool), ToggleGazeMode(), GetGazeMode()
C++ and Blueprint
Function library
UIsdkGazeFunctionLibrary::SetGazeMode(UObject*, bool), ToggleGazeMode(UObject*), GetGazeState(UObject*)
Blueprint, callable from any world context
Console command
Meta.InteractionSDK.ToggleGazeMode
Console
Sequence of a gaze mode toggle from an entry point through the pending request and reconciliation to OnGazeModeChanged.
GetGazeMode returns the previous value while the request is pending, and the new value once reconciliation applies it.
GetGazeMode() returns the applied state, not any pending request. To react to transitions, bind the OnGazeModeChanged multicast delegate, which passes a single bool bIsGazeMode. The following example binds the delegate and toggles gaze mode from an actor.
// MyPlayerActor.h
#include "Rig/IsdkGazeRigComponent.h"

UPROPERTY() UIsdkGazeRigComponent* GazeRig;
UFUNCTION() void HandleGazeModeChanged(bool bIsGazeMode);

// MyPlayerActor.cpp
void AMyPlayerActor::BeginPlay()
{
    Super::BeginPlay();
    GazeRig->OnGazeModeChanged.AddDynamic(this, &AMyPlayerActor::HandleGazeModeChanged);
    GazeRig->SetGazeMode(true); // applies on the next tick during reconciliation
}

void AMyPlayerActor::HandleGazeModeChanged(bool bIsGazeMode)
{
    // React to the applied transition (e.g., swap reticle, adjust UI).
}

Restoring the Rig When Gaze Mode Ends

Exiting gaze mode restores the rig to its prior configuration. The injected ray grab detector is removed by calling SetInjectedRayGrabDetector(nullptr) on each grabber, which reinstates the built-in ray detector. The allowance bitmask that was snapshotted into SavedGrabDetectorAllowances when gaze mode was entered is written back to each grabber and then cleared. CachedGrabbers is emptied. Nothing on the grabbers remains mutated by the gaze session.
Restore sequence clearing the injected ray grab detector, writing back the allowance snapshot, and emptying CachedGrabbers.
Clearing the injected detector reinstates the built-in ray detector before the allowance bitmask is written back.

Learn more

Now that you know how gaze mode interacts with hand and controller input, continue on to the following guides: