[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 を導入して見た目を置き換える

Starter Assets のプレイヤーに、VRM の “見た目だけ” を載せ替える章。 ここは今回の作業で最も壊れやすく、あなたが何度も破綻した部分なので、 Unityが確実に壊れない手順でまとめる。


2-1. UniVRM を Package Manager からインストール

公式 URL(https://github.com/vrm-c/UniVRM) → 最新の UnityPackage をダウンロード → Unity にドラッグ&ドロップで Import。

重要ポイント:

  • UniVRM は Starter Assets と共存しても問題なし
  • ただし VRMInstance の自動制御は後で削除する(重要)

2-2. VRM ファイルを取り込む

〇〇.vrm を Assets にドロップすると、自動的に以下が生成される:

AvatarName/
 ├ Meta
 ├ Materials
 ├ Prefab(←これを使う)
 └ Thumbnail

使うのは Prefab のみ。


2-3. VRM をシーンに直接置かない(重要)

あなたがやって破綻した例:

  • VRMを単体でシーンに置く → Tポーズ固定
  • VRM の Animatorが勝手に制御 → 二重アニメーション
  • VRMInstance が勝手に回転補正 → キャラがヌルヌル勝手に動く
  • 残像(ゴースト)が画面に残る → ダブルモデル問題

なので、VRM をそのまま動かすのは絶対にNG。


2-4. StarterAssets の見た目を削除する

まず PlayerArmature を展開し、 内部の “見た目だけのモデル” を削除する。

パス例:

PlayerArmature
 └ Geometry (←これを削除)

※ Geometry の中身は StarterAssets のシンプルな人体モデル。

ここで削除してもアニメーションは壊れない。 削除されるのは「Mesh」だけで、骨格(Armature)と Animator は残るため。


2-5. VRM のモデルを PlayerArmature に統合する

  1. VRM の Prefab を Project view から展開する(ダブルクリック)
  2. 展開された VRM の中にある “SkinnedMeshRenderer を持つモデル部分” だけを取り出す → だいたい階層はこんな感じ:
VRM-Model-Prefab
 └ A
    └ B
       └ C(Mesh / Materials)
  1. この “C (Mesh)” をドラッグして PlayerArmature / Armature / Hips 直下に置く。

StarterAssets では:

PlayerArmature
 └ Armature
     └ Hips
         └ (ここにVRMのメッシュを入れる)

2-6. VRMInstance を削除する(あなたが遭遇した問題点)

VRM を取り込むと、ほぼ確実に Prefab に VRMInstance がついている。

これが悪さをする:

  • 自動で LookAt 補正
  • Humanoid調整
  • 二重Animator
  • RootMotionバグ
  • ゴースト残像(あなたが遭遇)

✔ 解決:VRMInstance を完全削除

  1. VRM Prefab を選択
  2. Inspector の Add Component の下にある VRMInstance を Remove
  3. その他 VRM 由来の不要スクリプトも削除

あなたが実際にやって 残像が消えた 正しい処理。


2-7. PlayerArmature から VRM を正しく動かす

StarterAssets の Animator を使うので:

  • VRM 側の Animator は削除
  • Avatar 設定は StarterAssets の Humanoid を使用
  • VRM 側の Root は Hips に接続したのでアニメはそのまま流れる

結果:

  • VRM モデルが StarterAssets の動きを100%追従
  • 全アニメーションがそのまま適用
  • Tポーズ問題も二重アニメも解決

2-8. 動作確認

ここまで正しくできていれば:

  • VRM が StarterAssets のアニメで動く
  • WASD で走る・歩く・ジャンプ
  • マウスでカメラ操作
  • キャラの揺れや回転が自然

この段階でもう “PC版 VRM 3rdPersonController” が完成している。


✔ ここまでで得た結論(あなたが実体験したこと)

  • VRM を単体で動かしてはダメ
  • 必ず StarterAssets の骨格に載せる
  • VRMInstance は削除する
  • Animator は StarterAssets 側のみ使う
  • Hierarchy の構造が崩れると確実に破綻する
  • 正しい階層にメッシュを移植すると一発安定する

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 の見た目モデルもスムーズに統合できる