[Unity #16] Starter Assets + VRM + OpenXRを同一シーンで共存・判定切替

はじめに

Unity 2022で Starter Assets(3rdPersonController) と VRMモデル を組み合わせ、
さらに OpenXRベースのVR環境 を同一シーンで共存させて、
PCモード ↔ VRモードを自動判定して切り替えるまでを解説します。

ただし、この構成はそのままでは動作せず、カメラ競合・AudioListener競合・VR判定タイミングなど多数の地雷があります。本記事は開発中に遭遇した失敗とすべての解決策を 再現性ありでまとめたロードマップです。

前回の記事は以下:


動画: VR ➡ PC

1. Starter Assets を導入して PC 操作を最初に完成させる

  1. Package Manager から Starter Assets – Third Person Controller を追加。
  1. シーンへ PlayerArmature と PlayerFollowCamera を配置。
  1. シーン上で WASD とマウスで操作できることを確認。

この時点で PC用の 3rdPersonController が完成します。


2. VRM を導入して見た目を置き換える

  1. UniVRM を Package Manager 経由でインストール。
  2. VRM ファイルを Assets にドロップし、モデルを取り込む。
  3. StarterAssets の見た目部分を削除し、VRM の見た目だけを PlayerArmature に統合する。

Unityでは、VRMをそのまま置くのではなく、StarterAssets の「骨格 + アニメーション制御」を残しつつ VRM メッシュを載せるのが安定動作のコツです。


3. OpenXR と XR Interaction Toolkit を導入する

  1. Package Manager から OpenXR Plugin をインポート。
  2. XR Plug-in Management の Windows プラットフォームで OpenXR を有効に。
  3. Interaction Profile に Oculus Touch などを追加。
  4. シーンに XR Origin (XR Rig) を追加。

過去ログ:


4. カメラ・AudioListener・Cinemachineの衝突を回避する

Starter Assets 側の Camera は次のものを持つ:

  • Camera コンポーネント
  • AudioListener
  • CinemachineBrain

一方 VR 側の XR Origin も AudioListener を持つため、同一シーンでそのまま両方有効にすると競合して挙動が壊れます。


5. PC と VR のルートを分離する

PlatformManager
├─ PC Player Root
│   ├─ PlayerArmature
│   └─ PlayerFollowCamera
└─ VR Player Root
└─ XR Origin (XR Rig)

このように Root を分離し、どちらか一方だけをアクティブにする構成にします。

6. VR 判定は Awake ではなく Start で待機して行う

Unity の XR は Awake 時点ではまだ初期化されておらず判定できないため、 判定処理は Start コルーチンで数フレーム待つ必要があります。

以下が最終的に安定した PlatformSwitcher のコードです:

using UnityEngine;
using UnityEngine.XR;
using System.Collections;
using System.Collections.Generic;

public class PlatformSwitcher : MonoBehaviour
{
    [Header("PC (Starter Assets) 設定")]
    [SerializeField] private GameObject pcPlayerRoot;
    [SerializeField] private GameObject pcCamera;

    [Header("VR (XR Origin) 設定")]
    [SerializeField] private GameObject vrPlayerRoot;

    IEnumerator Start()
    {
        yield return new WaitForSeconds(0.1f);

        bool isVRActive = IsXRActive();

        if (isVRActive)
            SetupVR();
        else
            SetupPC();
    }

    private bool IsXRActive()
    {
        var displaySubsystems = new List<XRDisplaySubsystem>();
        SubsystemManager.GetInstances(displaySubsystems);
        foreach (var ds in displaySubsystems)
        {
            if (ds.running) return true;
        }

        if (XRSettings.isDeviceActive && !string.IsNullOrEmpty(XRSettings.loadedDeviceName))
            return true;

        return false;
    }

    private void SetupPC()
    {
        pcPlayerRoot.SetActive(true);
        pcCamera.SetActive(true);
        vrPlayerRoot.SetActive(false);
        Debug.Log("PlatformSwitcher: PCモードで起動しました");
    }

    private void SetupVR()
    {
        pcPlayerRoot.SetActive(false);
        pcCamera.SetActive(false);
        vrPlayerRoot.SetActive(true);
        Debug.Log("PlatformSwitcher: VRモードで起動しました");
    }
}

7. 遭遇した主なハマりポイントとその対処

VR 起動なのに PC モードになってしまう

Awake で VR 判定すると XR がまだ起動前のため false 固定になる → Start にして待機する。

真っ暗な画面(No cameras rendering)

PC 側カメラがアクティブなままだと VR 側のカメラが機能しない → PC カメラを VR 時に OFF。

AudioListener の競合

PC 側と VR 側の両方に AudioListener がある場合、警告が大量に出る → 片方だけ有効にする。

WASD は動くのに VR コントローラ入力が効かない

PC 側の Root が有効のまま → VR 時に完全に非アクティブにする。


8. 結果とまとめ

この構成を実装すると:

  • 単一のシーンで PC用 3rdPersonController と VR用 XR Origin が共存できる
  • 起動時に VR 判定して自動で切り替える
  • AudioListener / Camera 競合を完全に排除できる
  • VRM の見た目モデルもスムーズに統合できる