はじめに

VR用の衝突判定処理を実装したので、その備忘録メモ。

基本的に、non-VR で実装した処理とほぼ同じで、キャラクター座標は「playerRig」を起点にしていますが、 ルームスケールで動いた場合に座標がズレる問題について、AIに聞いてまとめています。

今回も動画取ってみました。分かりにくいですが衝突判定処理で、停止してます。 ただ、ルームスケールで動くと判定を超えて移動はできます。

概要

VRで衝突判定を実装しようとすると、 non-VRと同じ感覚では必ず混乱する。

  • 壁に当たって止まったはずなのに、現実で歩くと壁の向こうに行ける
  • UIは押せるのに、移動の当たり判定は別の場所で起きている
  • 「リグの位置で判定しているはずなのに?」という違和感

これはバグではなく、VR特有の構造によるもの。

この記事では、 Three.js + WebXR でのVR衝突判定を、 「なぜそう設計するのか」という視点で整理する。

VRには2種類の移動がある

まず、ここを分けないと必ず詰まる。

1. 人工移動(スティック移動)

  • コードで制御する移動
  • playerRig.position を動かす
  • 衝突判定の対象

2. 現実移動(ルームスケール)

  • 現実の身体の移動
  • HMD / コントローラのトラッキング
  • 衝突判定の対象外

VRでは、この2つは完全に別レイヤー

Three.js / WebXR の実際の構造

概念的にはこうなっている。

World
└─ playerRig(人工移動・衝突判定の基準)
   └─ XR Reference Space
      └─ camera / controller(現実移動で動く)

重要なのは:

  • 現実で歩いても playerRig は動かない
  • 動くのは camera.matrixWorldcontroller.matrixWorld
  • 衝突判定は playerRig 基準でしか行えない

なぜ現実移動で衝突判定しないのか

理由は単純で、止められないから

  • 壁があるからといって、頭の移動を止められない
  • 視点を強制的に押し戻すとVR酔いする
  • 安全面でも危険

そのため、すべてのVRゲームはこう割り切っている。

人工移動は制御する 現実移動は制御しない

これは three.js の制限ではなく、VRというメディアの仕様


VRで正しい衝突判定の基準

❌ やってはいけない

  • カメラ(視点)基準での衝突判定
  • コントローラ位置での移動制限
  • Matrix4 を使った位置衝突判定

⭕ 正解

  • playerRig を身体の代理として扱う
  • Box3 で「占有空間」を定義する
  • 移動前に予測判定する

Box3 と Matrix4 の役割の違い

ここを混ぜると混乱する。

Box3

  • 体積・領域
  • 衝突判定
  • 「ここに入っていいか」

Matrix4

  • 姿勢・向き・変換
  • レイキャスト
  • UI操作・向き計算

衝突判定に Matrix4 は使わない。 UIレイ用に使うのが正しい。


VR用 衝突判定の基本形

export function checkPreCollisionVR(moveVector) {
  const predictedPosition =
    config.playerRig.position.clone().add(moveVector);

  const playerSize = new THREE.Vector3(0.4, 1.7, 0.4);

  const predictedBox = new THREE.Box3();
  predictedBox.setFromCenterAndSize(
    new THREE.Vector3(
      predictedPosition.x,
      predictedPosition.y + playerSize.y / 2,
      predictedPosition.z
    ),
    playerSize
  );

  for (const wallBox of config.model.wallBoxes) {
    if (predictedBox.intersectsBox(wallBox)) {
      return false;
    }
  }
  return true;
}
  • 判定基準は playerRig
  • 視点の上下揺れは無視
  • 「止めるだけ」で反発しない

UIが押せてしまう理由

UI操作は、

  • controller.matrixWorld
  • レイキャスト

つまり 現実の手の位置を使っている。

一方で移動衝突判定は、

  • playerRig.position

このズレは仕様であり、 Meta / SteamVR / PSVR すべて同じ。


違和感への対処方法(完全解決は不可)

できるのは「緩和」だけ。

  • 壁に近づいたらフェード
  • 当たり判定を少し大きめに
  • UIは rig 基準距離に配置
  • 視点リセット(Recenter)を用意

「VRボタンで正面を更新」は業界標準。


まとめに近い事実

  • VRでは 現実の身体は完全に拘束できない
  • 衝突判定は人工移動だけに適用する
  • Box3 と Matrix4 は役割が違う
  • 違和感はバグではなく理解が進んだ証拠

この構造を理解していれば、 VRでの移動と衝突判定に迷わなくなる。