Claude
Skills
Sign in
Back

godot-platform-vr

Included with Lifetime
$97 forever

Expert blueprint for VR platforms (Meta Quest, PSVR, SteamVR, Pico) covering XR toolkit (OpenXR), comfort settings (vignetting, snap turning, teleport), motion controls, hand tracking, and 90+ FPS requirements. Use when targeting VR headsets or implementing immersive 3D experiences. Keywords VR, XR, OpenXR, Meta Quest, motion sickness, comfort, locomotion, XRController3D, foveated rendering.

Data & Analyticsscripts

What this skill does


# Platform: VR

90+ FPS, comfort-first design, and motion control accuracy define VR development.

## NEVER Do (Expert VR Rules)

### Rendering & Comfort
- **NEVER drop below 90 FPS** — In VR, 72 FPS or less causes instant nausea. You MUST maintain at least 90 FPS (Meta Quest 2/3 typical) and minimize rendering jank.
- **NEVER use smooth rotation without a vignette (comfort mask)** — Smooth rotation causes motion sickness. Always provide snap turning OR dynamic vignetting.
- **NEVER force 3D MSAA if Foveated Rendering is enabled** — Foveation can conflict with MSAA natively in the OpenXR pipeline on some hardware.

### Locomotion & Interaction
- **NEVER skip a teleport locomotion option** — Smooth movement is intolerable for many. Always offer teleportation as an accessibility alternative.
- **NEVER use billboarding for VR UI** — `BILLBOARD_ENABLED` breaks stereoscopic depth cues. Use static `MeshInstance3D` planes with `SubViewports`.
- **NEVER place UI too close or too far** — 0.5m causes eye strain; 10m is unreadable. Optimal distance is 1-3 meters from the player.

### Safety & System
- **NEVER forget to respect physical play area boundaries** — Stepping into real-world objects is a safety risk. Use `XRServer` to fetch guardian bounds.
- **NEVER ignore focus_lost or session_ended signals** — Gracefully handle disconnections or system menu overlays by pausing the simulation.
- **NEVER hardcode XRControllerTracker names** — Use the **OpenXR Action Map** system to decouple gameplay from specific hardware labels.

---

## Available Scripts

> **MANDATORY**: Read the appropriate script before implementing the corresponding pattern.

### [vr_openxr_initializer.gd](scripts/vr_openxr_initializer.gd)
Expert OpenXR initialization with driver support and feature verification.

### [vr_hand_gesture_detector.gd](scripts/vr_hand_gesture_detector.gd)
Pinch and Grab recognition using `XRHandModifier3D` for hand tracking.

### [vr_locomotion_handler.gd](scripts/vr_locomotion_handler.gd)
Expert Snap Turn and Comfort Vignette (Shader-less) implementation.

### [vr_passthrough_manager.gd](scripts/vr_passthrough_manager.gd)
Alpha blending and underlay setup for Mixed Reality (AR/VR) transitions.

### [vr_performance_config.gd](scripts/vr_performance_config.gd)
Expert Foveated Rendering and Variable Rate Shading (VRS) setup.

### [vr_haptic_sequencer.gd](scripts/vr_haptic_sequencer.gd)
Complex haptic pulse sequencing using `XRController3D` triggers.

### [vr_physics_hand_controller.gd](scripts/vr_physics_hand_controller.gd)
Non-clipping, physics-following hands that respect environmental solid.

### [vr_safety_guardian_warner.gd](scripts/vr_safety_guardian_warner.gd)
Guardian/Chaperone boundary distance warning logic using `XRServer`.

### [vr_headset_focus_guard.gd](scripts/vr_headset_focus_guard.gd)
Headset-aware pause logic for focus loss (System Menu / Headset Off).

### [vr_input_action_mapper.gd](scripts/vr_input_action_mapper.gd)
OpenXR Action Map abstraction to decouple logic from hwardware buttons.

---

```gdscript
# Enable XR
func _ready() -> void:
    var xr_interface := XRServer.find_interface("OpenXR")
    if xr_interface and xr_interface.initialize():
        get_viewport().use_xr = true
```

## Comfort Settings

- **Vignetting** during movement
- **Snap turning** (30°/45° increments)
- **Teleport locomotion** option
- **Seated mode** support

## Motion Controls

```gdscript
# XRController3D for hands
@onready var left_hand := $XROrigin3D/LeftController
@onready var right_hand := $XROrigin3D/RightController

func _process(delta: float) -> void:
    if left_hand.is_button_pressed("trigger"):
        grab_with_left()
```

## Performance

- **90 FPS minimum** - Critical for comfort
- **Low latency** - < 20ms motion-to-photon
- **Foveated rendering** if supported

## Best Practices

1. **Comfort First** - Prevent motion sickness
2. **High FPS** - 90+ required
3. **Physical Space** - Respect boundaries
4. **UI Distance** - 1-3m from player
### 1. Mixed-Reality-Passthrough Pattern (Quest 3)
To enable AR passthrough on devices like Meta Quest 3, set the `environment_blend_mode` to `ALPHA_BLEND`. This allows the virtual scene's alpha channel to control the visibility of the real-world camera feed.

```gdscript
class_name MixedRealityManager extends Node
## Switches the headset to AR/Passthrough mode.

func switch_to_ar() -> void:
    var xr_interface := XRServer.primary_interface
    if xr_interface and XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND in xr_interface.get_supported_environment_blend_modes():
        xr_interface.environment_blend_mode = XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND
        # Required for the camera feed to show through transparent areas.
        get_viewport().transparent_bg = true 
```

### 2. XR-Performance-Overlay (Composition Layers)
Standard 2D UI is blurry in VR due to lens distortion. Use `OpenXRCompositionLayerQuad` to project a `SubViewport` directly into the headset's runtime. This bypasses the 3D pipeline for a crisp, distortion-free display.

```gdscript
class_name XRPerformanceOverlay extends OpenXRCompositionLayerQuad
## A crisp UI overlay projected directly by the XR runtime.

@export var sub_viewport: SubViewport

func _ready() -> void:
    # Assign the viewport to the composition layer.
    layer_viewport = sub_viewport
    alpha_blend = true
    # Position in front of the user (relative to XROrigin3D).
    position = Vector3(0.0, 1.5, -1.0)
```

### 3. Universal-Grab-Manager (Decoupled Interactions)
Avoid hardcoding grab logic into interactables. Use the OpenXR Action Map to define a generic "grab" action and a central manager to handle reparenting based on group tags.

```gdscript
class_name UniversalGrabManager extends Node3D
## Decoupled interaction manager using OpenXR actions.

@export var controller: XRController3D
@export var grab_area: Area3D

var _grabbed: Node3D = null

func _ready() -> void:
    controller.button_pressed.connect(_on_grab)

func _on_grab(action_name: String) -> void:
    if action_name == "grab" and not _grabbed:
        for body in grab_area.get_overlapping_bodies():
            if body.is_in_group("grabbable"):
                _grabbed = body
                _grabbed.reparent(controller, true)
                break
```

## Reference
- Related: `godot-camera-systems`, `godot-input-handling`


### Related
- Master Skill: [godot-master](../godot-master/SKILL.md)

Related in Data & Analytics