見出し画像

SwitchのJoy-ConがApple Vision ProのVR対応の救世主だった

この記事は WebXR Advent Calendar 2025 6日目の記事です。

ブラウザで動くクロスプラットフォームメタバース「XRift」を開発しているのですが、前回の記事にあった通り今絶賛VR対応中です。MetaQuestでは最低限の対応が完了しました。
次は Apple Vision Pro 対応を考えていたのですが、コントローラーがない問題に直面しました。

Vision Proにはコントローラーがない

Vision ProはPSVR2コントローラーを公式サポートしていますが、単体での入手が難しく、他のVRコントローラーを繋ぐ手段もありません。ハンドトラッキングだけではVR空間を自由に移動するのが難しいためスティック入力は必須です。そのため「Vision ProのVR対応は諦めるか...」と思っていました。

ところがふとSwitchのコントローラーが目に入ったんですね。そして結論としては Switch の Joy-Con が Apple Vision Proにおいての最適解でした。

なぜJoy-Conを選んだのか

Nintendo Switch初代のJoy-Conを選んだ理由は:

  • 左右分割で使える - 両手に持ってスティック操作ができる

  • 安価に手に入りやすい - 中古も豊富、すでにお家にある人も多い

  • Bluetooth接続できる - 特別なドングル不要

  • 小さく手が隠れない - ハンドトラッキングと併用しやすい

  • Gamepad APIで認識される - WebXRで使いやすい

Nintendo Switch のコントローラーがまさか Gamepad API で使えるとは思っていなかったので驚きでした。ちなみにSwitch 2のJoy-Conはペアリングが不可能でした。

ただ気になるポイントはいくつかありました。

Gamepad APIで認識されるのは分かりましたが、以下の点が気になりました。

  • VR状態(immersive-vr)でも使えるのか?

  • ハンドトラッキングと併用できるのか?

  • コントローラーを握ったらハンドトラッキングが切れるんじゃないか?

特にハンドトラッキングとの併用が重要でした。Joy-ConはQuestコントローラーと違い、位置や回転の情報が取れません。アバターの手を動かすにはハンドトラッキングの情報に頼る必要があります。

気になるポイントは多かったですが、とりあえず試してみました。

window.addEventListener('gamepadconnected', (e) => {
  console.log(e.gamepad.id)
})

Vision ProにJoy-Conをペアリングして...

Joy-Con (L/R)

動きました。さらに詳細は次にお話しますが懸念していたハンドトラッキングとの併用も問題ありませんでした。

実装してみた

Joy-Conの入力を取得してプレイヤーを移動させるコードを書きました。

const detectJoyCons = () => {
  const gamepads = navigator.getGamepads()

  for (const gamepad of gamepads) {
    if (!gamepad) continue
    const id = gamepad.id.toLowerCase()

    if (id.includes('joy-con (l)')) {
      leftGamepad = gamepad  // 左スティック → 移動
    } else if (id.includes('joy-con (r)')) {
      rightGamepad = gamepad // 右スティック → 視点回転
    }
  }
}

左スティックで移動、右スティックで視点回転。Questコントローラーと同じ操作感です。

普通に遊べます。

落とし穴:統合モードで認識される

ただし1つハマりポイントがありました。

Joy-Con LとRをそれぞれペアリングしても、Vision Proでは1つのコントローラー(統合モード)として認識されます。

Joy-Con (L/R)

PCなどでは `Joy-Con (L)` と `Joy-Con (R)` が別々のGamepadとして認識されますが、Vision Proでは1つにまとまります。この場合、スティック軸のマッピングが変わります。

単体モード(PC等)

  • 左スティック → 左Gamepadの axes[0], axes[1]

  • 右スティック → 右Gamepadの axes[0], axes[1]

統合モード(Vision Pro)

  • 左スティック → axes[0], axes[1]

  • 右スティック → axes[2], axes[3]

両方に対応するコードにしました。

const id = gamepad.id.toLowerCase()

// 統合モードかどうかをIDで判定
const isCombinedMode = id.includes('l/r')

if (isCombinedMode) {
  // 統合モード: 1つのGamepadに両スティックが入っている
  const leftStick = { x: gamepad.axes[0], y: gamepad.axes[1] }
  const rightStick = { x: gamepad.axes[2], y: gamepad.axes[3] }
}

ハンドトラッキングの値の取得

Joy-Conでは位置・回転が取れないので、アバターの手を動かすにはハンドトラッキングを使います。WebXRでは、コントローラーとハンドトラッキングで取得方法が異なります。

function getTrackingSpace(inputSource: XRInputSource): XRSpace | null {
  // コントローラーの場合
  if (inputSource.gripSpace) {
    return inputSource.gripSpace
  }

  // ハンドトラッキングの場合
  if (inputSource.hand) {
    return inputSource.hand.get('wrist') ?? null
  }

  return null
}

`inputSource.hand` が存在すればハンドトラッキング、`inputSource.gripSpace` が存在すればコントローラーです。ハンドトラッキングの場合は `wrist`(手首)のジョイントから位置・回転を取得します。

Vision Proではハンドトラッキングが使えるので、Joy-Conを握っていてもちゃんと手の位置が取れました。

結果

Vision Pro + Joy-Conで、Questと同じようにVR空間を移動できるようになりました。

  • 左スティック:移動

  • 右スティック:視点回転

  • ボタン:ジャンプ、インタラクト

ハンドトラッキングも併用できるので、移動はJoy-Con、物を掴むのは手、という使い分けも可能です。

まとめ

Switch Joy-Con のおかげで Apple Vision Pro で動くおそらく世界初のメタバースを作ることができました。Vision Pro をお持ちの方でしたらぜひ XRift で遊んでみてください。


いいなと思ったら応援しよう!