FERRETGame engine over Babylon.js
Ferret

VR in a Ferret game

Skill ferret-xr · download all skills · SKILL.md

When to use#

A game should run in a headset, a VR control scheme is needed, something must be grabbed or pointed at, or the frame time in the headset is off.

The model#

VR is opt-in and the game does not change: createGame({ xr: { enabled: true } }) loads @ferret/xr by import() only where navigator.xr exists (never on OS Beroun). The headset is the camera, the capsule stays the player, UIService is drawn on panels in the world, Interactor gets ray and touch sources, and Grabbable gives grabbing on the screen and in the headset.

Steps#

  1. Pass xr: { enabled: true, storageKey: '<game>' } to createGame; add the samples' addEnterVrButton(game) (a session must start from a click) and honour ?xr.
  2. Add xr bindings for the game's existing actions (xrStick, xrButton, xrPinch); keep action names, never branch game code on VR.
  3. Keep turn and teleport as the action names or set xr.actions; leave snap turn and vignette on by default.
  4. Put every piece of text through UIService; own DOM elements are not seen in the headset.
  5. Add Grabbable (with a RigidBody) to objects to take; set maxMass, snapPoints, twoHanded.
  6. Do loading (glTF, shapes, NavMesh) behind a fade; check budgets with xr.status and the section on budgets in the guide.

Check afterwards#

xr.status on a screen reports unsupported and the game plays as before; unit tests with a fake XrDriver; Playwright with an emulated headset; on a Quest: comfort first, then frame time (13.9 ms Quest 2, 11.1 ms Quest 3).

What usually goes wrong#

  • Assuming a feature (hand tracking, layers, multiview): they are optional; read xr.status.
  • Starting a session without a user click: it is refused.
  • Pushing the view back from a wall or moving the camera in code: the headset owns the head; use blink and fades.
  • Removing snap turn or the vignette, or dropping the "In VR" settings page.
  • Loading a heavy level in free play: a dropped frame in both eyes.
  • Mapping buttons "like the console": the headset has its own kind (xr); do not reuse cursor/A/B keys.

Files#

  • reference/xr.md — Turn a game into a VR game with one option: head and hands, controller bindings, comfort settings, menus in the world, grabbing, budgets for Quest.
  • reference/api-xr.md — every export of @ferret/xr with signature and doc comment
  • reference/api-interaction.md — every export of @ferret/interaction with signature and doc comment
  • reference/commands.md — 4 commands of these packages with their arguments