LeapMotionでLive2Dモデルの指を動かす

# はじめに
Unity(C#)でLeapMotionから指の角度を取得してLive2Dモデルをリアルタイムに動かす方法

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">こうですか！！<br>思ったより簡単に指の曲がり具合がとれました！！！(個人差でパラメータ調整は必要かもしれないですあくまで私の手に最適化されてます)<br>使用モデルは鳥総帥さん(<a href="https://twitter.com/bird_w_cooking?ref_src=twsrc%5Etfw">@bird_w_cooking</a>)<br>の製作中モデルの腕をお借りしました <a href="https://twitter.com/hashtag/leapmotion?src=hash&amp;ref_src=twsrc%5Etfw">#leapmotion</a> <a href="https://twitter.com/hashtag/Live2D?src=hash&amp;ref_src=twsrc%5Etfw">#Live2D</a> <a href="https://twitter.com/hashtag/Unity?src=hash&amp;ref_src=twsrc%5Etfw">#Unity</a> <a href="https://t.co/U1mGrNsQTk">pic.twitter.com/U1mGrNsQTk</a></p>&mdash; ときなし (@KalsaKey) <a href="https://twitter.com/KalsaKey/status/1204757101970485249?ref_src=twsrc%5Etfw">December 11, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# 環境
Unity : 2019.2.2f1
Live2D SDK : 4.0-beta.2
LeapMotion SDK : 4.4.0
Live2Dモデル書き出しファイル　: 以下画像
<img width="215" alt="qiita1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554095/4d7a6089-17a9-1326-3734-d04b3ae8e6b0.png">
今回使用した腕モデルはLive2Dイラストレーター・モデラーの鳥総帥様から許可を得て使用しています

#準備
## Live2D
公式チュートリアルページの[SDKをインポート](https://docs.live2d.com/cubism-sdk-tutorials/getting-started/)の通りにSDKとモデルをインポート
## LeapMotion
[Unity向けSDK](https://developer.leapmotion.com/unity#5436356)からUnityPackageをインポート
**ここからが問題なんですよね**
大抵はLeapHandControllerプレハブをヒエラルキーに入れると書いてあるんですが、少なくとも私の環境ではインポートの問題か何回試してもスクリプトがmissingだったため使えませんでした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554095/92124746-9a1a-d3c9-1a3e-04752c1a638a.png)
なのでSDK付属のサンプルシーンからコピペします。
参考にしたのは以下の記述です。
>Assets/LeapMotion/Core/Examples/Capsule Hands(Desktop)からLeapMotionControllerとHandModelsをコピペして自分のシーンに貼り付けます。
[LeapMotion+Unityでグー・チョキ・パーを認識する](https://qiita.com/Hirai0827/items/bc0224393cbfa1551e8a)　より引用

# 実際のコード
このスクリプトをLive2Dモデルにアタッチします

```cs:MovingArm.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Leap;
using Live2D.Cubism.Core;
using Live2D.Cubism.Framework;

public class MoveArm : MonoBehaviour
{
    private CubismModel _model;
    private Controller controller;
    private Dictionary<Leap.Finger.FingerType, CubismParameter> modelfingers;

    // Start is called before the first frame update
    void Start()
    {
        controller = new Controller();
        _model = this.FindCubismModel();

        modelfingers = new Dictionary<Leap.Finger.FingerType, CubismParameter>();
        modelfingers.Add(Leap.Finger.FingerType.TYPE_INDEX, _model.Parameters[1]);
        modelfingers.Add(Leap.Finger.FingerType.TYPE_MIDDLE, _model.Parameters[2]);
        modelfingers.Add(Leap.Finger.FingerType.TYPE_RING, _model.Parameters[3]);
        modelfingers.Add(Leap.Finger.FingerType.TYPE_PINKY, _model.Parameters[4]);
        modelfingers.Add(Leap.Finger.FingerType.TYPE_THUMB, _model.Parameters[5]);
    }
    private void LateUpdate()
    {
        Frame frame = controller.Frame();

        if (frame.Hands.Count != 0)
        {
            List<Hand> hand = frame.Hands;
            var fingers = hand[0].Fingers;
            foreach (Finger finger in fingers)
            {
                if (finger.Type == Leap.Finger.FingerType.TYPE_UNKNOWN) continue;

                var angle = Mathf.PI - finger.Direction.AngleTo(hand[0].Direction);
                var param = EditParam(angle, 0f, Mathf.PI, -30, 30);
                
                if (finger.Type == Leap.Finger.FingerType.TYPE_THUMB)
                {
                    angle = finger.Direction.AngleTo(hand[0].PalmNormal);
                    param = EditParam(angle, 1.0f, 1.27f, -30, 30);
                    Debug.Log("THUMB " + angle + " " + param);
                }

                modelfingers[finger.Type].Value = param;
            }
        }
    }
    private float EditParam(float param,float leapmin,float leapmax, float modelmin,float modelmax)
    {
        return (param - leapmin) * ((modelmax - modelmin) / (leapmax - leapmin)) + modelmin;
    }
}

```

#解説
##using
LeapMotionの機能を使いたい→　`using Leap;`
Live2Dの機能を使いたい→ `using Live2D.Cubism.Core;`
`using Live2D.Cubism.Framework;`

##modelfingers って辞書はなに
Fingerクラスに取得したFinger型オブジェクトの指の種類（親指・人差し指・中指・薬指・小指・不明）が取得できる[FingerType](https://leapmotion.github.io/UnityModules/class_leap_1_1_finger.html#a10a29d6e6abd79a9d6c8ebad0ba18b9d)があるので、モデルの各指パラメータの順番に合わせて（例えば今回モデルの人差し指は2番目の位置にあるので0開始で```_model.Parameters[1]```）各指の種類とモデルのパラメータオブジェクトを当てはめてる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554095/52a0c152-5dde-7156-9e85-ca948e4aa86d.png)

##angleってなに
`finger.Direction`で指の差す角度（Vector）
`hand[0].Direction`で手先の角度（Vector）
`AngleTo() `で各ベクトル間の角度を取得
今回モデルの動き（Angleパラメータ最大（30）で指伸ばし最小（-30）で指曲げ）とAngleToで取得できる数値が逆だったため180度(`Mathf.PI`)で反転してます

##EditParam(angle, 1.0f, 1.27f, -30, 30);
**すいません取得した値をログでひろってきて値の最大最小を直接入力するゴリ押しをしました**
親指だけ曲がる角度が大きく違うので……
ここはもっと方法があると思います

#最後
間違い、説明不足な点などありましたらコメントか編集リクエストをお願いします。