UnityでVtuber用の表情制御リグを作る

#自己紹介
こんにちはゆずです。@Yuzu_Unity


##はじめに
Vtuberの表情制御の仕方はいくつかあると思いますが
自分なりの一番いいシステムを考えてみました。
リアルタイム＆あとから修正にも対応

まず欲しい機能としては
OVRリップシンク
手付（コントローラーでキャプチャも含む）
iPhone等でのフェイシャルトラッキング(キャプチャー含む)

また、それらのブレンド具合の調整・レコード機能・Timelineでプレイモード外での表示

ここまであれば他に望むものは無いかなーと思います。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="da" dir="ltr">VtuberFacialRigDemo <a href="https://t.co/aK3fh9cmpI">pic.twitter.com/aK3fh9cmpI</a></p>&mdash; ゆず (@Yuzu_Unity) <a href="https://twitter.com/Yuzu_Unity/status/1123290758209888256?ref_src=twsrc%5Etfw">2019年4月30日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>




##設計
基本的には3DCGのブレンドシェイプ式によるフェイシャルリグを参考に作成してます。

FacialRigのパラメータのブレンド具合をFacialWeightで管理します。
配列が使えないため（レコードシステム参照）かなり面倒ではある
![Untitled Diagram (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308905/cfa4503a-79df-bdd5-f8cd-9bd469a64eef.png)



##レコードシステム
いくつかの記事を見ているとなぜか、jsonで保存していたりなど
自作していることが多いのですが、処理も重かったりとなかなか使いづらいです。

※Unityには標準でアニメーションクリップへのレコード機能があるんです！！(Editor専用)
**GameObjectRecorder** (※UnityのバージョンによりAPIが異なるため公式リファレンス参照)
https://docs.unity3d.com/2018.3/Documentation/ScriptReference/Animations.GameObjectRecorder.html
これを知るとみんな乗り換えそ～（棒）
**もちろん処理の負荷もなく、フレームでの差分がないとキーフレームも打たれません。**

しかし…ちょっと使いづらいんですよね
コンポーネントをキャプチャーするとその差分全部をキャプチャーしてしまい、
いらないところの排除ができない…（やり方あったら教えてほしい…）
なので別でキャプチャー専用コンポーネント（C#クラスを作成します）
Unityのアニメーションでスクリプトの変数を動かしたことがある人はわかると思うのですが
キーフレームが打たれる条件は次のとおりです。

・シリアライズされていること（publicもしくは[SerializeField]）
・配列やリストではないこと…。　配列使えないのがとてもめんどくさい…
（エディタ拡張を使ってもいい解決ができない…キーフレームが普通に打てなくなったりします）

BlendShapeの全パラメータ等キーフレームを打ちたい数分、変数を置きます。
また別の配列に入れ直します。

```c#:FacialRigcs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//プレイモード外でも実行
[ExecuteInEditMode]
public class FacialRig : MonoBehaviour
{
    [Range(0, 100)]
    public int a, i, u, e, o, blink;

    [HideInInspector]
    public int[] facilValue=new int[6];
    

    void Update()
    {
        facilValue[0] = a;
        facilValue[1] = i;
        facilValue[2] = u;
        facilValue[3] = e;
        facilValue[4] = o;
        facilValue[5] = blink;
    }
}
```

あとはレコード用のスクリプトを用意して完成です。
プレイ時レコードスタート

```c#:RecordFacialRig.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//忘れずに
using UnityEditor.Animations;
public class RecordFacialRig : MonoBehaviour
{
    public AnimationClip clip;
    private GameObjectRecorder gameObjectrecorder;

    void Start()
    {
        // Animatorコンポーネントをつけるオブジェクト
        gameObjectrecorder = new GameObjectRecorder(gameObject);
       
        /* ここにバインドしたいコンポーネント記述（今回はFacialRig
        このスクリプトがアタッチしているオブジェクトからGetComponentを行う）*/
        gameObjectrecorder.BindComponentsOfType<FacialRig>(gameObject, true);
    }
    
    void LateUpdate()
    {
        if (clip == null)
            return;

        // フレームごとにキーフレームを打つ
        gameObjectrecorder.TakeSnapshot(Time.deltaTime);
    }

    void OnDisable()
    {
        if (clip == null)
            return;

        if (gameObjectrecorder.isRecording)
        {
            // アニメーションクリップに保存
            gameObjectrecorder.SaveToClip(clip);
        }
    }
}

```

以下はOVRLipSyncを編集しFacialRigにパラメータを流しそれをキャプチャーしたもの
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308905/16724c36-ca24-f978-9de4-8f15e3a26afa.png)


##設計をもとにFacialManagerスクリプトを作成
最後にFacialManagerでパラメータのブレンドを行い、メッシュへ適用します。
FacialRig.csのfacilValueを用い、FacialWeightのweightValueを乗算したものを
加算しClampを行うだけの簡単な作業なのでコードは省略
※[ExecuteInEditMode]は忘れないようにします。

FacialRigコンポーネントをTimelineに設置しキャプチャーした各種AnimationTrackを配置した図
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308905/f0499784-057f-6d8d-b610-8cfdc6ae48dd.png)


iPhoneX系による表情取得方法は後日記述