【VTuber】Oculus touchで表情を変えてみる


OculusTouchのボタンを使って表情を変える方法を紹介します。

ボタンの入力操作につきましては、こちらの記事を参考にさせていただきました。
https://framesynthesis.jp/tech/unity/touch/


##完成イメージ
![kizunaai_facecontroller_gif.gif](https://qiita-image-store.s3.amazonaws.com/0/242611/dc23b02b-ee24-d771-4101-b962ce2f69d2.gif)

##用意するもの
- モデル(今回は[キズナアイ](http://kizunaai.com/download-page/)を使用させていただきました)
- [Unity](https://unity3d.com/jp)
- [Oculus Utilities for Unity](https://developer.oculus.com/downloads/package/oculus-utilities-for-unity-5/)
- [Oculus lift](https://www.oculus.com/rift/)

##制作の流れ
ボタンを押したときの処理を書く
↓
アタッチしたオブジェクトの、SkinnedMeshRendererの値を指定する

以上。
##ボタンに応じて表情を変更させる

OculusTouchを使うにはOVRのライブラリを使わないといけません。

ということで、[Oculus Utilities for Unity](https://developer.oculus.com/downloads/package/oculus-utilities-for-unity-5/)をプロジェクトにインポートします。

そしたらOVR > Prefab > **OVRcameraRig**を適当に配置しましょう。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/242611/ae2527ed-be61-2c5c-327c-4107acc7e8fd.png)



次にボタンに関するスクリプトを用意ます。今回はFaceController.csとしました。

以下のコードを書いて、モデルやカメラにアタッチしてください。（どこでも構いません）

```FaceController.cs
public class FaceController : MonoBehaviour {
    public SkinnedMeshRenderer skinnedMeshRenderer = null;
    // Use this for initialization
    void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {
        //押したときの処理
        if (OVRInput.GetDown(OVRInput.RawButton.A))
        {
            //喜
            Debug.Log("Aボタンを押した");
            skinnedMeshRenderer.SetBlendShapeWeight(0, 100f);
        }
        if (OVRInput.GetDown(OVRInput.RawButton.B))
        {
            //怒
            Debug.Log("Bボタンを押した");
            skinnedMeshRenderer.SetBlendShapeWeight(25, 100f);
        }
        if (OVRInput.GetDown(OVRInput.RawButton.X))
        {
            //哀
            Debug.Log("Xボタンを押した");
            skinnedMeshRenderer.SetBlendShapeWeight(7, 100f);
        }
        if (OVRInput.GetDown(OVRInput.RawButton.Y))
        {
            //楽
            Debug.Log("Yボタンを押した");
            skinnedMeshRenderer.SetBlendShapeWeight(22, 100f);
        }
        if (OVRInput.GetDown(OVRInput.RawButton.Start))
        {
            //死んだ目
            Debug.Log("メニューボタン（左アナログスティックの下にある）を押した");
            skinnedMeshRenderer.SetBlendShapeWeight(30, 100f);
        }

        if (OVRInput.GetDown(OVRInput.RawButton.RIndexTrigger))
        {
            //右ウィンク
            Debug.Log("右人差し指トリガーを押した");
            skinnedMeshRenderer.SetBlendShapeWeight(3, 100f);
        }
        if (OVRInput.GetDown(OVRInput.RawButton.RHandTrigger))
        {
            //まばたき
            Debug.Log("右中指トリガーを押した");
            skinnedMeshRenderer.SetBlendShapeWeight(1, 100f);
        }
        if (OVRInput.GetDown(OVRInput.RawButton.LIndexTrigger))
        {
            //左ウィンク
            Debug.Log("左人差し指トリガーを押した");
            skinnedMeshRenderer.SetBlendShapeWeight(2, 100f);
        }
        if (OVRInput.GetDown(OVRInput.RawButton.LHandTrigger))
        {
            //じと目
            Debug.Log("左中指トリガーを押した");
            skinnedMeshRenderer.SetBlendShapeWeight(8, 100f);
        }

        //離したときの処理
        if (OVRInput.GetUp(OVRInput.RawButton.A))
        {
            Debug.Log("Aボタンを離した");
            skinnedMeshRenderer.SetBlendShapeWeight(0, 0f);
        }
        if (OVRInput.GetUp(OVRInput.RawButton.B))
        {
            skinnedMeshRenderer.SetBlendShapeWeight(25, 0f);
        }
        if (OVRInput.GetUp(OVRInput.RawButton.X))
        {
            skinnedMeshRenderer.SetBlendShapeWeight(7, 0f);
        }
        if (OVRInput.GetUp(OVRInput.RawButton.Y))
        {
            skinnedMeshRenderer.SetBlendShapeWeight(22, 0f);
        }
        if (OVRInput.GetUp(OVRInput.RawButton.Start))
        {
            skinnedMeshRenderer.SetBlendShapeWeight(30, 0f);
        }

        if (OVRInput.GetUp(OVRInput.RawButton.RIndexTrigger))
        {
            skinnedMeshRenderer.SetBlendShapeWeight(3, 0f);
        }
        if (OVRInput.GetUp(OVRInput.RawButton.RHandTrigger))
        {
            skinnedMeshRenderer.SetBlendShapeWeight(1, 0f);
        }
        if (OVRInput.GetUp(OVRInput.RawButton.LIndexTrigger))
        {
            skinnedMeshRenderer.SetBlendShapeWeight(2, 0f);
        }
        if (OVRInput.GetUp(OVRInput.RawButton.LHandTrigger))
        {
            skinnedMeshRenderer.SetBlendShapeWeight(8, 0f);
        }
    }
}
```

すると**Skinned Mesh Renderer**にオブジェクトを入れる欄があるので、ここに顔に当たるオブジェクトをアタッチしておきます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/242611/400873f9-c746-8d1e-4434-3ea7e65581c2.png)
スクリプトに顔を受け取ることで、 `skinnedMeshRenderer.SetBlendShapeWeight(1, 100f);` で自由に動かせるようになりました。

`skinnedMeshRenderer.SetBlendShapeWeight(1, 100f);` にある、`(1, 100f)` に注目してください。

この第一引数(ここでいう1)が、表情のインデックス番号、第二引数(ここでいう100f)がインデックス番号の値になります。

このインデックス番号については、スクリプトにアタッチしたオブジェクト > BlendShapesから確かめることが可能。
![index_number.PNG](https://qiita-image-store.s3.amazonaws.com/0/242611/e3034d1a-9870-3a65-42f3-137b157e3931.png)

キャラによって種類や数が違いますが、一番左の番号がインデックス番号で、それぞれ値を変更できることが分かると思います。

例えば、笑いを100、「あ」の口の形を100にしてみると、モデルもそれにしたがって表情が変更されることに気づくかと思います。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/242611/76ebac0f-2f05-1009-9fbf-d9f0d87774e4.png)

ぜひ一度確かめてみてください。


後はボタンに応じて、モーフのインデックス番号、それに対する値をそれぞれ指定するだけですね。

実際にボタンを押して指定した表情になるのか確かめてみてください。

##まとめ
口に関してはマイクからリップシンクさせつつ、表情はボタンで変えるということをしてみました。

今回はキズナアイモデルを使いましたが、キャラによって顔のオブジェクトの場所、表情の種類が違うので、別のキャラを使う方には各自探していただく形になるかもしれません。

もし音声からもリップシンクさせたい！という方はそちらもまとめましたので参考にしていただければと。
https://qiita.com/ngron/items/b2c02a0f8133aac2ad9a