ここでは、XRIT と Rigidbody 版の全体的なアーキテクチャの比較をしつつ、ここまで触れていない Grab移動と登りの Rigidbody 版についても確認していく。


1. アーキテクチャ概要

1.1 XRIT のアーキテクチャ

XR Origin
 └─ LocomotionMediator          ← 各 Provider の状態機械を管理
     ├─ XRBodyTransformer       ← Transformation キューを毎フレーム適用
     ├─ GravityProvider         ← 重力を一元管理(球キャスト接地判定)
     ├─ ContinuousMoveProvider  ← スティック移動(CharacterController 経由)
     ├─ GrabMoveProvider        ← グラブ移動(コントローラー差分を XR Origin に反映)
     └─ ClimbProvider           ← 登り(インタラクタブル選択イベント経由)

移動の流れ:
Provider → TryQueueTransformation()XRBodyTransformerXROriginMovement.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 ContinuousMoveProviderRigidbodyMoveProvider
衝突処理CharacterController.Move()Rigidbody + CapsuleCollider(物理エンジン)
重力GravityProvider が球キャストで接地判定し速度を蓄積物理エンジンのネイティブ重力(Y 速度に手を出さない)
移動適用タイミングUpdate()Update() で入力、FixedUpdate() で velocity 書き込み
空中制御inAirControlModifier で減衰未実装(常に同じ速度)
飛行モードenableFly フラグなし
移動方向基準forwardSource(カメラまたは任意 Transform)左右手ごとに Head/Hand Relative を選択可能
ステート管理Idle→Preparing→Moving→Endedbool なし(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 GrabMoveProviderRigidbodyPhysicsGrab
グラブ検出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 ClimbProviderRigidbodyPhysicsGrab
対象物の要件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/RotationFixedUpdate で更新
物理演算なし(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 が代行)
  • 重力・衝突をすべて物理エンジンに委任するため、このプロジェクトの範囲ではコード量が少ない
  • 物理的なリアクションが自然(ジョイントの張力が手触りを生む)

カスタム実装の現在の制約(今後の課題):

  1. 複数グラブ対応(両手クライム)
  2. クライム中の重力抑制(現在は Joint の張力が重力に抗うのみ)
  3. LocomotionState に相当する協調制御
  4. 軸制約(登り棒など縦方向のみの移動)