Signatur
Beschreibung
Die Methode forEach() von XRInputSourceArray durchläuft alle aktuell mit einer XRSession verbundenen Eingabequellen – etwa Hand-Controller, getrackte Hände, Blick-Eingaben (Gaze) oder Bildschirm-Touches bei AR. Für jede Eingabequelle wird der übergebene Callback mit dem Element, seinem Index und dem Array selbst aufgerufen.
Ein XRInputSourceArray erhält man über XRSession.inputSources. Es ist kein echtes Array, sondern ein Array-ähnliches, live aktualisiertes Objekt mit length, Index-Zugriff sowie den Methoden entries(), keys(), values() und forEach(). Methoden wie map() oder filter() stehen nicht direkt zur Verfügung.
Typischerweise nutzt man forEach() innerhalb des Render-Loops (requestAnimationFrame-Callback), um für jeden Controller die Pose per XRFrame.getPose() abzufragen, einen Zeigestrahl zu zeichnen oder Gamepad-Tasten auszuwerten.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $callback Pflicht | (currentValue: XRInputSource, currentIndex: number, sourceList: XRInputSourceArray) => void | Funktion, die für jede Eingabequelle aufgerufen wird. Erhält currentValue (die aktuelle XRInputSource), currentIndex (deren Index) und sourceList (das durchlaufene XRInputSourceArray). Ein Rückgabewert wird ignoriert. |
|
| $thisArg | any | undefined | Wert, der innerhalb von callback als this verwendet wird. Bei Pfeilfunktionen wirkungslos. |
Beispiele
Alle Eingabequellen einer Sitzung auflisten
const session = await navigator.xr.requestSession('immersive-vr');
session.inputSources.forEach((source, index) => {
console.log(`#${index}: ${source.handedness} / ${source.targetRayMode}`, source.profiles);
});
Zeigestrahl-Posen im Render-Loop abfragen
const onXRFrame = (time, frame) => {
const session = frame.session;
session.requestAnimationFrame(onXRFrame);
session.inputSources.forEach((source) => {
const pose = frame.getPose(source.targetRaySpace, refSpace);
if (!pose) return; // Controller gerade nicht getrackt
const { position } = pose.transform;
drawRay(source.handedness, pose.transform.matrix);
console.log(source.handedness, position.x.toFixed(2), position.y.toFixed(2), position.z.toFixed(2));
});
};
let refSpace;
const session = await navigator.xr.requestSession('immersive-vr');
refSpace = await session.requestReferenceSpace('local');
session.requestAnimationFrame(onXRFrame);
Gamepad-Tasten aller Controller auswerten
const checkButtons = (session) => {
session.inputSources.forEach((source) => {
// Hand-Tracking oder Gaze liefern kein Gamepad
if (!source.gamepad) return;
const [trigger, squeeze] = source.gamepad.buttons;
if (trigger?.pressed) {
console.log(`Trigger gedrückt (${source.handedness})`);
}
if (squeeze?.pressed) {
console.log(`Griff gedrückt (${source.handedness})`);
}
});
};
Mit thisArg auf ein Zustandsobjekt schreiben
const stats = { hands: 0, controllers: 0 };
session.inputSources.forEach(function (source) {
if (source.hand) this.hands++;
else this.controllers++;
}, stats);
console.log(stats);
// Wichtig · Fallstricke
- Kein echtes Array:
map(),filter()odersome()fehlen. Bei Bedarf mitArray.from(session.inputSources)oder Spread-Syntax[...session.inputSources]umwandeln. - Kein vorzeitiger Abbruch:
returnim Callback überspringt nur das aktuelle Element;breakist nicht möglich. Für Abbruch einefor...of-Schleife verwenden. - Live-Liste: Der Inhalt ändert sich, wenn Controller verbunden oder getrennt werden. Auf Änderungen reagiert man am besten über das Ereignis
inputsourceschangederXRSession, statt Referenzen dauerhaft zu speichern. - Pose kann
nullsein:XRFrame.getPose()liefertnull, wenn eine Quelle vorübergehend nicht getrackt wird – immer prüfen. gamepadist optional: Gaze-, Screen- und teils Hand-Eingaben haben keingamepad-Objekt.- Kompatibilität: WebXR erfordert einen sicheren Kontext (HTTPS) und wird v. a. von Chromium-basierten Browsern und dem Meta Quest Browser unterstützt; Firefox und Safari (außer visionOS) bieten keine bzw. nur eingeschränkte Unterstützung. Die Methode ist als experimentell gekennzeichnet.
- Performance: Im Render-Loop keine neuen Closures oder Objekte pro Frame anlegen, wenn es sich vermeiden lässt – das reduziert Garbage-Collection-Ruckler in VR.