Start · Sprachen · JavaScript · Referenz · XRInputSource.gamepad

XRInputSource.gamepad

Eigenschaft

Liefert ein <code>Gamepad</code>-Objekt mit dem aktuellen Zustand der Tasten und Achsen eines XR-Controllers oder <code>null</code>, wenn die Eingabequelle keine solchen Bedienelemente hat.

seit JavaScript WebXR Gamepads Module Level 1 – Chrome/Edge 79+, S Kategorie: webapi

Signatur

XRInputSource.gamepad

Beschreibung

Die schreibgeschützte Eigenschaft gamepad einer XRInputSource liefert ein Gamepad-Objekt, über das du Tasten (buttons), Analogsticks und Touchpads (axes) eines XR-Controllers auslesen kannst. Sie stammt aus dem WebXR Gamepads Module und bildet die Brücke zwischen WebXR und der bekannten Gamepad-API.

Anders als bei klassischen Gamepads taucht dieses Objekt nicht in navigator.getGamepads() auf. Es gehört ausschließlich zur jeweiligen Eingabequelle und ist nur während einer aktiven XRSession gültig. Der Wert von gamepad.mapping ist bei XR-Controllern "xr-standard", sofern das Gerät dem Standard-Layout entspricht: buttons[0] = Trigger, buttons[1] = Griff (Squeeze), buttons[2] = Touchpad, buttons[3] = Thumbstick; axes[0/1] = Touchpad X/Y, axes[2/3] = Thumbstick X/Y.

Eingabequellen ohne Tasten oder Achsen – etwa Blickrichtungssteuerung (targetRayMode === "gaze"), Bildschirm-Touch oder reines Hand-Tracking – liefern null. Für einfache Aktionen wie „Auswählen“ und „Greifen“ solltest du daher bevorzugt die Ereignisse select und squeeze der Session nutzen und gamepad nur für zusätzliche Bedienelemente wie Thumbsticks abfragen.

Da der Zustand nicht per Event gemeldet wird, fragst du ihn typischerweise in jedem Frame innerhalb des Callbacks von XRSession.requestAnimationFrame() ab (Polling).

Rückgabewert

Typ
Gamepad | null
Beschreibung
Ein Gamepad-Objekt mit buttons, axes, mapping (meist "xr-standard") und timestamp – oder null, wenn die Eingabequelle keine Tasten/Achsen besitzt.

Beispiele

Thumbstick und Trigger pro Frame auslesen

const session = await navigator.xr.requestSession('immersive-vr');
const refSpace = await session.requestReferenceSpace('local');

const onFrame = (time, frame) => {
  for (const source of session.inputSources) {
    const gp = source.gamepad;
    if (!gp) continue; // z. B. Blicksteuerung oder Hand-Tracking

    const [, , stickX = 0, stickY = 0] = gp.axes;
    const trigger = gp.buttons[0];

    if (Math.abs(stickX) > 0.2 || Math.abs(stickY) > 0.2) {
      console.log(`${source.handedness}: Stick ${stickX.toFixed(2)} / ${stickY.toFixed(2)}`);
    }
    if (trigger?.pressed) {
      console.log(`${source.handedness}: Trigger ${trigger.value.toFixed(2)}`);
    }
  }
  session.requestAnimationFrame(onFrame);
};

session.requestAnimationFrame(onFrame);
right: Stick 0.85 / -0.10 right: Trigger 1.00

Tastendruck erkennen (Flanke statt Dauerzustand)

const previous = new WeakMap(); // XRInputSource -> bisherige Tastenzustände

const handleButtons = (session) => {
  for (const source of session.inputSources) {
    const gp = source.gamepad;
    if (!gp) continue;

    const before = previous.get(source) ?? [];
    gp.buttons.forEach((btn, i) => {
      if (btn.pressed && !before[i]) {
        console.log(`Taste ${i} am ${source.handedness}-Controller gedrückt`);
      }
    });
    previous.set(source, gp.buttons.map((b) => b.pressed));
  }
};

// Aufruf innerhalb des XR-Frame-Callbacks:
// session.requestAnimationFrame((t, frame) => { handleButtons(session); ... });
Taste 4 am left-Controller gedrückt

Controller-Fähigkeiten beim Verbinden prüfen

session.addEventListener('inputsourceschange', ({ added }) => {
  for (const source of added) {
    const gp = source.gamepad;
    if (!gp) {
      console.log(`${source.targetRayMode}: kein Gamepad – nur select/squeeze verfügbar`);
      continue;
    }
    console.log(
      `${source.handedness} | Mapping: ${gp.mapping} | ` +
      `${gp.buttons.length} Tasten, ${gp.axes.length} Achsen | Profile: ${source.profiles.join(', ')}`
    );
  }
});
right | Mapping: xr-standard | 7 Tasten, 4 Achsen | Profile: oculus-touch-v3, oculus-touch-v2, generic-trigger-squeeze-thumbstick

// Wichtig · Fallstricke

  • Immer auf null prüfen: Blicksteuerung, Touch-Eingaben (AR auf Smartphones) und Hand-Tracking liefern meist kein Gamepad.
  • Nicht in navigator.getGamepads(): XR-Controller erscheinen dort nicht, und das Ereignis gamepadconnected wird für sie nicht ausgelöst. Neue Controller erkennst du über inputsourceschange.
  • Layouts unterscheiden sich: Welche Indizes über das xr-standard-Layout hinaus belegt sind (z. B. A/B/X/Y ab buttons[4]), hängt vom Gerät ab. Mit source.profiles und der Bibliothek WebXR Input Profiles kannst du die Belegung zuverlässig zuordnen. Fehlende Bedienelemente werden als Platzhalter-Einträge bzw. mit dem Wert 0 gemeldet.
  • Totzone: Analogsticks liefern im Ruhezustand selten exakt 0. Setze einen Schwellwert (etwa 0,1–0,2).
  • Nur Momentaufnahme: Der Zustand gilt nur während der Session. Werte nach Möglichkeit im Frame-Callback lesen und keine Referenzen über das Session-Ende hinaus aufbewahren.
  • Kompatibilität: Wird von Chromium-Browsern (Chrome, Edge, Samsung Internet) und dem Meta Quest Browser unterstützt. Firefox (Desktop) und Safari unterstützen es nicht. Safari auf der Apple Vision Pro bietet WebXR nur eingeschränkt an, meist ohne Gamepad-Daten. WebXR setzt einen sicheren Kontext (HTTPS) voraus.