Develop

Hands Train sample overview

Updated: May 11, 2026

Overview

This sample demonstrates near-field poke and far-field ray-based hand tracking interaction patterns in Unreal Engine through a toy train scene. It implements priority routing between interaction modes, pinch state machines, focus hysteresis, and collidable interactable state management. The scene includes a toy locomotive, crossing gate, and windmill that respond to hand interactions.

What you will learn

  • Implement near-field (poke) and far-field (ray) interaction with priority routing
  • Bind finger bone capsules from UOculusXRHandComponent for poke detection
  • Build ray-based interaction with cone fallback and focus hysteresis
  • Create a pinch state machine for grab detection
  • Configure custom collision channels for interaction zones
  • Build collidable interactable actors with state machines

Requirements

  • Meta Quest 2, Quest 3, or Quest 3S
  • Unreal Engine configured for Meta Quest development
For setup instructions, see the Meta Quest Developer Hub documentation.

Get started

Clone the repository from GitHub, open the project in Unreal Engine, and build for Android. The project uses three custom collision channels and implements both near-field and far-field interaction systems. For detailed build and configuration steps, see the project README.

Explore the sample

ComponentWhat it demonstratesKey concepts
AFingerTipPokeTool
Near-field poke interaction using bone capsules
UOculusXRHandComponent->CollisionCapsules binding
ARayTool
Far-field ray interaction with cone fallback
GetPointerPose(), 20° cone fallback, 1.2x focus hysteresis
PinchStateModule
Pinch detection state machine
None→PinchDown→PinchStay→PinchUp transitions
AControllerBox
HMD-following controller container
Max distance threshold, position tracking
ACollidableInteractable
Interactable state machine
Default→Proximity→Contact→Action states
Toy train scene
Interactive environment
Locomotive, crossing gate, windmill

Runtime behavior

When running on a Meta Quest device, the sample presents a toy train scene with a locomotive, crossing gate, and windmill. Near-field poke interactions activate when your fingertip collides with an interactable surface—AFingerTipPokeTool binds bone capsules from UOculusXRHandComponent->CollisionCapsules to detect contact. Far-field ray interactions use ARayTool with GetPointerPose() for aiming; when the ray cannot find a target, a 20° cone fallback expands the search area. Focus hysteresis (1.2x multiplier) prevents rapid switching between nearby targets. The PinchStateModule tracks pinch through four states: None, PinchDown, PinchStay, and PinchUp. AControllerBox follows the HMD position with a maximum distance threshold. ACollidableInteractable transitions through Default, Proximity, Contact, and Action states.
Note: On Quest 2, GPU-intensive scenes may require reduced geometry or simplified shaders due to the lower GPU budget compared to Quest 3.

Key concepts

Near-field poke interaction

AFingerTipPokeTool creates collision capsules bound to finger bones from the OculusXR hand component:
// AFingerTipPokeTool binds collision capsules from the hand component
// CollisionCapsules array maps to fingertip bones
`UOculusXRHandComponent`* HandComp = GetHandComponent();
TArray<FCapsuleInfo>& Capsules = HandComp->CollisionCapsules;
// Each capsule follows its bone transform for frame-accurate poke detection

Far-field ray interaction

ARayTool implements ray-based selection with a cone fallback and focus hysteresis:
// Primary ray from GetPointerPose()
// If no hit within ray, expand to 20° cone for easier acquisition
// Focus hysteresis: selected target stays active within 1.2x the selection distance
// This prevents rapid focus switching between adjacent targets
float FocusHysteresis = 1.2f;

Pinch state machine

PinchStateModule tracks pinch state through discrete transitions:
// Pinch states: None → PinchDown → PinchStay → PinchUp → None
// PinchDown: pinch strength exceeds activation threshold
// PinchStay: pinch held above release threshold
// PinchUp: pinch strength drops below release threshold
enum class EPinchState { None, PinchDown, PinchStay, PinchUp };

Custom collision channels

The project defines three custom collision channels for interaction routing:
// Three custom collision channels:
// 1. FarField - used by ARayTool for ray and cone traces
// 2. HandCapsuleOrTool - used by AFingerTipPokeTool for poke detection
// 3. InteractableCollisionZone - defines interactable trigger volumes
// Priority routing: near-field (poke) takes precedence over far-field (ray)

Collidable interactable state machine

ACollidableInteractable manages interaction through four states:
// State transitions:
// Default: no hand interaction detected
// Proximity: hand enters InteractableCollisionZone
// Contact: fingertip capsule touches interactable surface
// Action: pinch or poke completes the interaction
enum class EInteractableState { Default, Proximity, Contact, Action };

Extend the sample

  • Add new interactable objects to the train scene using ACollidableInteractable.
  • Adjust the cone fallback angle and focus hysteresis multiplier for different interaction distances.
  • Create new pinch-based gestures by extending PinchStateModule with additional states.
  • Optimize the scene for Quest 2 by reducing draw calls and simplifying materials.