UniVRMのVRMBlendShapeProxyで表情アニメーション処理を共通化する

#はじめに
2018/4/16にドワンゴさんからVRM及びUniVRMが公開されました。
詳細は[公式のgithub](https://dwango.github.io/vrm/)を参照してください（とても丁寧にまとめられているので基本的なことはここを読めば大丈夫だと思います。）
VRMはVRアプリケーション向けの人型3Dアバター（3Dモデル）データを扱うためのファイルフォーマットであり、UniVRMはUnity向けのVRMファイルの読み書きを行うC#による標準実装とあります。
さて、UniVRMはUnity向けのVRMファイルの読み書きを行うC#による標準実装とあるように以下のコンポーネントが提供されています。

- モデル情報
- BlendShape
- 一人称視点
- 視線制御
- 揺れモノ

つまり、VRMのセットアップ済モデルであれば上記の情報が共通の処理で取得できるということになります。便利です。
本記事ではUniVRMにおけるBlendShapeの操作方法について解説したいと思います。

#環境
- Unity2017.4.0f1
- UniVRM-0.30

#UniVRMからBlendShapeが取得できなかったら
そもそもUniVRMでBlendShapeを取得することの何が便利なのでしょうか。
下の画像はリップシンクによく使うOVRLipSyncのOVRLipSyncContextMorphTargetスクリプトのインスペクタです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/60734/3ccef1cb-b2d6-ec1b-3259-710823ff02c5.png)

Visime To Blend Targetsの10～14にあ～おのShape Keyを設定するのですが、モデル毎にSkinned Mesh Rendererの名前も場所もバラバラ、またShape Keyの順番もバラバラなので、これを設定するときはモデルの構造を熟知しないと設定できません。色々なモデルを扱おうとすると辛い。。。（設定法については[凹みさんの記事](http://tips.hecomi.com/entry/2016/02/16/202634)）参照
そこでUniVRMのVRMBlendShapeProxyを通してShape Keyを取得し、操作するというのが本記事のテーマです。

#UniVRMからBlendShapeを操作する

今回はToggleで表情（「あいうえお」、「喜」、「哀」）を操作する簡単なスクリプトを作成しました。

```c#:FaceAnimation.cs
using UnityEngine;
using UnityEngine.UI;
using VRM;

public class FaceAnimation : MonoBehaviour
{

    private VRMBlendShapeProxy proxy;
    public GameObject model; // VRMモデルのオブジェクトを設定
    public Toggle a;
    public Toggle i;
    public Toggle u;
    public Toggle e;
    public Toggle o;
    public Toggle egao;
    public Toggle komari;


    // Use this for initialization
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {

        // VRMBlendShapeProxyはUpdateが始まってからでないと取得できない
        if (proxy == null)
        {
            proxy = model.GetComponent<VRMBlendShapeProxy>();
        }
        else
        {

            // あ
            if (a.isOn)
            {
                proxy.SetValue(BlendShapePreset.A, 1f);
            }
            else
            {
                proxy.SetValue(BlendShapePreset.A, 0f);
            }

            // い
            if (i.isOn)
            {
                proxy.SetValue(BlendShapePreset.I, 1f);
            }
            else
            {
                proxy.SetValue(BlendShapePreset.I, 0f);
            }

            // う
            if (u.isOn)
            {
                proxy.SetValue(BlendShapePreset.U, 1f);
            }
            else
            {
                proxy.SetValue(BlendShapePreset.U, 0f);
            }

            // え
            if (e.isOn)
            {
                proxy.SetValue(BlendShapePreset.E, 1f);
            }
            else
            {
                proxy.SetValue(BlendShapePreset.E, 0f);
            }

            // お
            if (o.isOn)
            {
                proxy.SetValue(BlendShapePreset.O, 1f);
            }
            else
            {
                proxy.SetValue(BlendShapePreset.O, 0f);
            }

            // 笑顔
            if (egao.isOn)
            {
                proxy.SetValue(BlendShapePreset.Joy, 1f);
            }
            else
            {
                proxy.SetValue(BlendShapePreset.Joy, 0f);
            }

            // 困り顔
            if (komari.isOn)
            {
                proxy.SetValue(BlendShapePreset.Sorrow, 1f);
            }
            else
            {
                proxy.SetValue(BlendShapePreset.Sorrow, 0f);
            }
        }
    }
}

```

ここでのポイントはUpdate関数内でproxyを設定しているところです。現在の仕組みだとUpdateが走ってからでないとproxy.SetValue()で操作できません。（さらに正確にいうとVRMBlendShapeProxy内部のBlendShapeMergerのインスタンスが設定されないと操作できない）

```c#
    void Update()
    {

        // VRMBlendShapeProxyはUpdateが始まってからでないと取得できない
        if (proxy == null)
        {
            proxy = model.GetComponent<VRMBlendShapeProxy>();
        }
```

VRMBlendShapeProxyの取得さえできてしまえばあとはSetValue()でBlendShapeの値を変更できます。（値の範囲は0f～1fです）

```c#
proxy.SetValue(BlendShapePreset.A, 1f);
```
#動かしてみる
今回はUnityちゃんをVRMにセットアップして試してみます。（© Unity Technologies Japan/UCL）
[公式](https://dwango.github.io/vrm/univrm/components/univrm_blendshape/)を参考に「あいうえお」と「喜」に笑顔、「哀」に困り顔をセットアップしました。(マテリアル周りの設定は割愛しています)
あとはシーンをセットアップして実行してみます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/60734/84324008-1a39-7ffe-688c-9c0309b16c5d.png)

#実行結果
しっかり動いてくれました！
![ダウンロード (1).gif](https://qiita-image-store.s3.amazonaws.com/0/60734/850e9524-5cbc-26a7-d828-384175d37212.gif)

#モデル変更
さらに応用（というかこれがやりたかった）としてモデルの変更をやってみます。
変更するモデルはニコニ立体ちゃんをお借りしました。
http://3d.nicovideo.jp/works/td32797

VRMファイルをインポートしてできたプレハブをシーンに配置してFaceAnimation.csのmodelにインスペクタに設定するだけで・・・
![ダウンロード2.gif](https://qiita-image-store.s3.amazonaws.com/0/60734/67c58c60-3f48-5706-fa64-452c6f4e2c0f.gif)

ちゃんと動いてくれました。モデル変更も数ステップで行けますね！

#終わりに
まだ公開されたばかりでVRMに対応したモデルは少ないですが、今後VRMが普及していくと簡単にモデルを入れ替えて共通の動作をさせることができます。モデルのセットアップは簡単なので、自作モデルがある方は是非作ってみてください！あとプログラマの方はVRMのAPIに対応した処理を作っておくと幸せになれるかもですね。