ここでは、XRIT と Rigidbody 版の全体的なアーキテクチャの比較をしつつ、ここまで触れていない Grab移動と登りの Rigidbody 版についても確認していく。
1. アーキテクチャ概要
1.1 XRIT のアーキテクチャ
XR Origin
└─ LocomotionMediator ← 各 Provider の状態機械を管理
├─ XRBodyTransformer ← Transformation キューを毎フレーム適用
├─ GravityProvider ← 重力を一元管理(球キャスト接地判定)
├─ ContinuousMoveProvider ← スティック移動(CharacterController 経由)
├─ GrabMoveProvider ← グラブ移動(コントローラー差分を XR Origin に反映)
└─ ClimbProvider ← 登り(インタラクタブル選択イベント経由)
移動の流れ:
Provider → TryQueueTransformation() → XRBodyTransformer → XROriginMovement.Apply() → CharacterController.Move() または transform.position
1.2 カスタム Rigidbody のアーキテクチャ
XR Origin (Rigidbody + CapsuleCollider)
└─ RigidbodyMoveProvider ← スティック移動(linearVelocity 直接操作)
Hand (Rigidbody)
└─ ConfigurableJoint ← connectedBody = XR Origin Rigidbody または コントローラー Rigidbody
└─ RigidbodyJointBinder ← 起動時にトラッキング開始を検知してジョイント接続
└─ RigidbodyHandBinder ← targetPosition/Rotation を毎 FixedUpdate 更新
Hand に付く(グラブ時)
└─ FixedJoint → 掴んだ物体/KinematicAnchor
└─ RigidbodyPhysicsGrab ← グラブ入力を検出して FixedJoint を生成/破棄
移動の流れ(グラブ移動・登り):
手の FixedJoint が壁を固定 → 手が動けない → ConfigurableJoint の張力が XR Origin を引き寄せる → 物理的登り
2. 各コンポーネントの対比
2.1 連続移動 — ContinuousMoveProvider vs RigidbodyMoveProvider
| 項目 | XRIT ContinuousMoveProvider | RigidbodyMoveProvider |
|---|---|---|
| 衝突処理 | CharacterController.Move() | Rigidbody + CapsuleCollider(物理エンジン) |
| 重力 | GravityProvider が球キャストで接地判定し速度を蓄積 | 物理エンジンのネイティブ重力(Y 速度に手を出さない) |
| 移動適用タイミング | Update() | Update() で入力、FixedUpdate() で velocity 書き込み |
| 空中制御 | inAirControlModifier で減衰 | 未実装(常に同じ速度) |
| 飛行モード | enableFly フラグ | なし |
| 移動方向基準 | forwardSource(カメラまたは任意 Transform) | 左右手ごとに Head/Hand Relative を選択可能 |
| ステート管理 | Idle→Preparing→Moving→Ended | bool なし(enabled/disabled のみ) |
| 重力との連携 | IGravityController 経由で GravityProvider をロック | なし(物理エンジンに委任) |
XRIT の移動計算(核心部):
// ContinuousMoveProvider.cs:282
var translationInRigSpace = forwardRotation * m_InAirVelocity * speedFactor;
var translationInWorldSpace = originTransform.TransformDirection(translationInRigSpace);
// → CharacterController.Move(motion) に渡すRigidbody 版の移動計算(核心部):
// RigidbodyMoveProvider.cs:95-98
var v = m_Rigidbody.linearVelocity;
v.x = m_DesiredHorizontalVelocity.x;
v.z = m_DesiredHorizontalVelocity.z;
m_Rigidbody.linearVelocity = v; // Y(重力・ジャンプ)は物理エンジンに委ねる2.2 グラブ移動 — GrabMoveProvider vs RigidbodyPhysicsGrab
| 項目 | XRIT GrabMoveProvider | RigidbodyPhysicsGrab |
|---|---|---|
| グラブ検出 | IXRSelectInteractor の選択イベント(Ray/Direct Interactor) | OverlapSphere で最近傍コライダーを毎フレームチェック |
| 対象物体の要件 | ClimbInteractable コンポーネントは不要(Grab は任意オブジェクト) | LayerMask でフィルタ(専用コンポーネント不要) |
| 移動メカニズム | コントローラーのローカル位置差分を XR Origin に逆方向へ加算 | FixedJoint で手を固定 → ConfigurableJoint の張力で XR Origin を引っ張る |
| 移動量の計算 | previousPos - currentPos を World 空間に変換 | 物理演算(Joint の stiffness/damper パラメータで調整) |
| 移動スケール | moveFactor で倍率調整可能 | Joint パラメータ(spring/damper)で調整 |
| 選択中の移動 | enableMoveWhileSelecting フラグで制御 | MoveProvider.enabled を false にして抑制 |
| 多様な MoveProvider との連携 | LocomotionMediator が Grab と Move を調停 | m_MoveProvider.enabled = false で直接無効化 |
XRIT の移動計算(核心部):
// GrabMoveProvider.cs:145-147
var move = originTransform.TransformVector(
m_PreviousControllerLocalPosition - controllerLocalPosition
) * m_MoveFactor;
// → ConstrainedMoveProvider.MoveRig() → CharacterController.Move()Rigidbody 版の移動メカニズム(グラブ時):
// RigidbodyPhysicsGrab.cs:97-99
m_GrabJoint = m_HandRigidbody.gameObject.AddComponent<FixedJoint>();
m_GrabJoint.connectedBody = targetRb; // 壁や Kinematic アンカーに接続
// → 物理的拘束で XR Origin が引き寄せられる(コードに移動ロジックなし)2.3 登り — ClimbProvider vs RigidbodyPhysicsGrab(Climb 動作)
| 項目 | XRIT ClimbProvider | RigidbodyPhysicsGrab |
|---|---|---|
| 対象物の要件 | ClimbInteractable + ClimbInteractor(専用コンポーネント必須) | LayerMask でフィルタされた任意コライダー |
| グラブ検出 | Interactable の OnSelectEntered/Exited イベント | OverlapSphere |
| 移動メカニズム | アンカー位置(クライマブル空間)とインタラクタ現在位置の差分を XR Origin に加算 | FixedJoint による物理拘束(GrabMove と同じ仕組みで climb も実現) |
| 軸制約 | ClimbSettings.allowFreeX/Y/ZMovement で各軸の移動を制限可能 | 未実装 |
| 複数グラブ | 対応(最新のグラブが移動を主導) | 未実装(最初の FixedJoint のみ) |
| 重力制御 | GravityProvider.TryLockGravity(ForcedOff) でクライム中は重力オフ | 物理エンジンの重力がそのまま作用(FixedJoint が抵抗) |
| クライム終了後の重力 | enableGravityOnClimbEnd で落下を制御 | FixedJoint 破棄後は即座に重力が作用 |
| クライム中の他 Provider 抑制 | providersToDisable リストで明示的に管理 | m_MoveProvider.enabled = false のみ |
XRIT のクライム移動計算(核心部):
// ClimbProvider.cs:257
movement = m_InteractorAnchorWorldPosition - interactorWorldPosition;
// → アンカー位置に手を戻すように XR Origin を動かす(FixedJoint と同じ概念をソフトウェアで実装)2.4 手の追従 — XR Controller (Transform) vs RigidbodyHandBinder
| 項目 | XRIT(XR Controller / TrackedPoseDriver) | RigidbodyHandBinder |
|---|---|---|
| 追従方法 | TrackedPoseDriver が直接 Transform を上書き(物理なし) | ConfigurableJoint.targetPosition/Rotation を FixedUpdate で更新 |
| 物理演算 | なし(Transform 直接操作) | あり(Rigidbody が Joint を通じて追従) |
| 追従遅延 | なし(即座) | spring/damper パラメータに依存(1〜2 FixedUpdate 分の遅延) |
| 衝突 | なし(手が壁を貫通) | あり(手のコライダーが壁に衝突・押し返し) |
| 機能 | 純粋なトラッキング | トラッキング + 物理的な押し返し + Grab/Climb の力の伝達 |
3. 重力システムの比較
XRIT GravityProvider
CheckGrounded(): 頭から下方向へ球キャスト(SphereCastNonAlloc)- 接地時:
m_CurrentFallVelocity = Vector3.zeroにリセット - 空中時:
m_CurrentFallVelocity += gravity * gravityAccelerationModifier * deltaTime - Climb/GrabMove 中:
TryLockGravity(ForcedOff)で重力ロック IGravityControllerインターフェースで各 Provider が重力を協調制御
カスタム Rigidbody
- 物理エンジン(Rigidbody.useGravity = true)に全委任
- 接地判定は CharacterController を持たず物理衝突に依存
- Climb 中でも重力が作用(FixedJoint の breakForce=∞ が抵抗)
- 実装が大幅に簡潔だが、クライム終了時の挙動(落下速度の制御)が困難
4. アーキテクチャ上の重要な差異
4.1 移動のメカニズムの根本的な違い
XRIT:
コントローラー位置の差分 → ソフトウェアで XR Origin を移動
(物理エンジンは使わない)
Rigidbody 版:
FixedJoint(手と壁)+ ConfigurableJoint(手と Origin)→ 物理エンジンが Origin を引く
(移動の計算はコード上に存在しない)
4.2 LocomotionMediator の有無
XRIT は LocomotionMediator が「誰が今移動権を持っているか」を状態機械で管理する。
カスタム実装は enabled フラグの直接制御に依存しており、複数の Provider が同時に有効な場合の競合を手動で解決する必要がある。
4.3 Kinematic Anchor の役割
RigidbodyPhysicsGrab が生成する KinematicAnchor(Rigidbody の isKinematic = true)は、XRIT の ClimbInteractable に相当する。コライダーに Rigidbody がない場合の「仮想的なアタッチポイント」として機能する。
5. 現実装の制約と未実装事項
| 機能 | XRIT | カスタム Rigidbody | 備考 |
|---|---|---|---|
| 連続移動 | ✅ | ✅ | Rigidbody 版は空中制御なし |
| グラブ移動 | ✅ | ✅ | Rigidbody 版は Joint で実現 |
| 登り(Climb) | ✅ | ✅(GrabMove と共通実装) | Rigidbody 版は軸制約なし |
| 軸制約 | ✅ | ❌ | |
| 複数グラブ | ✅ | ❌ | |
| 重力ロック(クライム中) | ✅ | ❌ | 物理エンジン重力が常に作用 |
| 接地判定 | ✅(球キャスト) | 物理エンジン依存 | |
| クライム終了後の重力制御 | ✅ | ❌ | |
| LocomotionState 管理 | ✅ | ❌ | |
| 手の物理コライダー | ❌ | ✅ | Rigidbody 版の独自強み |
| 手の押し返し力 | ❌ | ✅ | Rigidbody 版の独自強み |
6. まとめ
どちらのアーキテクチャが優れているかではなく、プロジェクトの要件によって適切な選択が変わる。以下は私のプロジェクトで感じた特徴を整理したものだ。
XRIT の特徴:
- LocomotionMediator による複数 Provider の協調制御が組み込まれている
- GravityProvider による精密な重力制御(クライム中ロック・終了後の制御)が利用できる
- ClimbSettings による軸制約が標準で提供されている
- 状態管理が明確(状態機械)で大規模プロジェクトでの予測可能性が高い
カスタム Rigidbody の特徴(このプロジェクトにおける利点):
- 手に物理コライダーがあり「壁を触る感触」を表現できる
- 移動ロジックをほぼ書かずに Grab/Climb を実現(Joint が代行)
- 重力・衝突をすべて物理エンジンに委任するため、このプロジェクトの範囲ではコード量が少ない
- 物理的なリアクションが自然(ジョイントの張力が手触りを生む)
カスタム実装の現在の制約(今後の課題):
- 複数グラブ対応(両手クライム)
- クライム中の重力抑制(現在は Joint の張力が重力に抗うのみ)
- LocomotionState に相当する協調制御
- 軸制約(登り棒など縦方向のみの移動)