UnityでVRMファイルをインポートして使うときの覚え書き

# はじめに

2018年4月に、ドワンゴが「[VRM](https://dwango.github.io/vrm/)」という新しい3Dアバターフォーマットを公開しました。
今回はこのVRMをUnity上で利用する方法についてのメモ書きです。
（随時追加します。）

なお、この記事は「VRMファイルをUnity上でどう扱うか」についての記事です。
3DモデルをVRMファイルに変換する話はしません。


# 一緒にみると良いかもしれない資料

 * [ARでVRアバターを表示するシステムを構築しよう(ニコナレ)](https://niconare.nicovideo.jp/watch/kn3105)
 * [AR_VR_Viewer(github)](https://github.com/TORISOUP/AR_VR_Viewer)

以前作った、[VRのアバターをARで操作するシステム(twitter)](https://twitter.com/toRisouP/status/996761692456435712)について解説した資料です。
この資料でも触れているVRMについての情報をこの記事は含んでいます。


# 使い方メモ

## 0.UniVRMの各種使い方など

まずは[公式ドキュメント](https://dwango.github.io/vrm/)を読みましょう。
下の方に[BlendShape(表情変更)](https://dwango.github.io/vrm/univrm/api/univrm_use_blendshape/)のやり方についての説明とかあります。


## 1. UniVRMの導入

UnityでVRMを利用する場合はUniVRMを用いることになるでしょう。
公式でサポートされているVRMインポータ/エクスポータです。

* [UniVRM](https://github.com/dwango/UniVRM)

導入方法は簡単で、[release](https://github.com/dwango/UniVRM/releases)ページから最新のunitypackageをダウンロードして導入すればOKです。
なお、2018年6月時点ではUniVRMは週に1～2回の頻度でアップデートが実行されています。
アップデートのたびに微妙に仕様が変更されたり、バグって壊れたりすることがあるので小まめにバージョンを確認することを推奨します。


## 2. VRMモデルのインポート方法

VRMファイルをUnityで利用する方法は2パターンあります。

* Prefab化する
* ランタイムロードする

### Prefab化する方法

VRMファイルをUnityの`/Assets`以下に配置した場合、自動的にUniVRMがインポートしてPrefabに変換してくれます。
ビルドにVRMモデルを埋め込んでしまう場合はこのPrefabを直接使う方法が楽です。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/47146/6d5935fc-4a2a-2b4e-ed23-e759fb2c4016.png)

### ランタイムロードする方法

VRMをスクリプトからランタイムロードすることで、動的にVRMモデルをGameObjectとしてシーン上に出現させることができます。
ランタイムロードする場合は、VRMファイルを`StreamingAssets`などに配置するとよいでしょう。

```cs:一番単純なロード方法
void Start()
{
    var path = Application.streamingAssetsPath + "/" + "AliciaSolid.vrm";

    // Actionコールバックで生成されたGameObjectが返される
    VRMImporter.LoadVrmAsync(path, gameObject =>
    {
        gameObject.transform.position = new Vector3(1, 1, 1);
    });
}
```

なお、UniVRMが`v0.37`以降であれば`async/await`を用いてロード待ちをすることができます。

```cs:async/await
async void Start()
{
    var path = Application.streamingAssetsPath + "/" + "AliciaSolid.vrm";

    // awaitでロード待機
    var gameObject = await VRMImporter.LoadVrmAsync(path);

    gameObject.transform.position = new Vector3(1, 1, 1);
}
```

#### おすすめのロード方法

`WWW`を使って先に`byte[]`ファイルを読み込んでしまい、それを`VRMImporter.LoadVrmAsync`に渡す方法を推奨します。
理由は次の2点です。

 * `VRMImporter.LoadVrmAsync`が内部で`File.ReadAllBytes`を使用しており、これが同期読み込みのため重い(v0.38時点)
 * `StreamingAssets`以下のファイルはAndroid実機の場合は`WWW`経由じゃないとアクセスできない

```cs:WWWを使ったロード方法
IEnumerator LoadVrmCoroutine(string path, Action<GameObject> onLoaded)
{
    var www = new WWW(path);
    yield return www;
    VRMImporter.LoadVrmAsync(www.bytes, onLoaded);
}

void Start()
{
    var path = Application.streamingAssetsPath + "/" + "AliciaSolid.vrm";

    StartCoroutine(LoadVrmCoroutine(path, go =>
    {
        go.transform.position = new Vector3(1, 1, 1);
    }));
}
```

ちなみに、`UniRx`を導入して`async/await`と組み合わせるとこう書けます。最高では？

```cs:UniRx+async/await+WWW
async void Start()
{
    var path = Application.streamingAssetsPath + "/" + "AliciaSolid.vrm";

    var www = new WWW(path);

    await www;

    var go = await VRMImporter.LoadVrmAsync(www.bytes);

    go.transform.position = new Vector3(1, 1, 1);
}
```

## 3.指の曲げ伸ばし

いろいろ手法があると思うけど、自分はこのスクリプトを`AddComponent`して使っています。

 * [FingerController](https://github.com/TORISOUP/AR_VR_Viewer/blob/master/Assets/VrmArPlayer/Scripts/Scene/Main/Common/FingerController.cs)

```cs:使い方
// 対象アバターのGameObjectのrootにアタッチ
var fingerController = gameObject.AddComponent<FingerController>();

// 左手全部曲げる
fingerController.FingerRotation(FingerController.FingerType.LeftAll, 1.0f);

// 右手の人差し指だけ伸ばしてあと全部曲げる
fingerController.FingerRotation(FingerController.FingerType.RightAll, 1.0f);
fingerController.FingerRotation(FingerController.FingerType.RightIndex, 0.0f);
```

![1.gif](https://qiita-image-store.s3.amazonaws.com/0/47146/348ee388-6858-5654-b539-4aec41ee6464.gif)
両手全部曲げ伸ばししてる画像（Lerp入れるとなめらかになってよさそう）


# とりあえずここまで

発見があれば追記します