LeapMotion+Unityでグー・チョキ・パーを認識する

#はじめに
VRMの流行の兆しが見えたので推しの子とじゃんけんが出来るソフトでも作っておこうかな、と思ったのでLeapMotionでじゃんけんの手を検知する仕組みを作りました。知見共有。
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">今日のおもちゃ：LeapMotionでじゃんけんの手を認識する <a href="https://t.co/rLF0GXOh9F">pic.twitter.com/rLF0GXOh9F</a></p>&mdash; 避雷 (@lucknknock) <a href="https://twitter.com/lucknknock/status/1091879285307015169?ref_src=twsrc%5Etfw">2019年2月3日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

#制作
新しい方のLeapMotionSDKとUnityの知見は少な目な気がします…もしかしてもうLeapMotion界隈下火だったりします…？

##LeapMotionSDKの準備
まずLeapMotion公式から[Unity向けSDK](https://developer.leapmotion.com/unity/#5436356)をダウンロードしておきます。中にあったUnityPackageをProjectにインポートします。
##Unity側の準備
Assets/LeapMotion/Core/Examples/Capsule Hands(Desktop)からLeapMotionControllerとHandModelsをコピペして自分のシーンに貼り付けます。コレさえあればとりあえず動くはずなので、試しに実行してみて手がトラッキングできればとりあえず成功です。
##手の形を認識する

```C#:HandRSP.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Leap;
using System.Linq;
public class HandRSP : MonoBehaviour {
    private Controller controller;
    private Finger[] fingers;
    private bool[] isGripFingers;
    public enum RSP
    {
        Rock, Scissors, Paper
    };
    public RSP rsp;
	// Use this for initialization
	void Start () {
        controller = new Controller();
        fingers = new Finger[5];
        isGripFingers = new bool[5];
	}
	
	// Update is called once per frame
	void Update () {
        Frame frame = controller.Frame();
        if(frame.Hands.Count != 0)
        {
            List<Hand> hand = frame.Hands;
            fingers = hand[0].Fingers.ToArray();
            isGripFingers = Array.ConvertAll(fingers, new Converter<Finger, bool>(i => i.IsExtended));
            Debug.Log(isGripFingers[0]+","+ isGripFingers[1] + "," + isGripFingers[2] + "," + isGripFingers[3] + "," + isGripFingers[4]);
            int extendedFingerCount = isGripFingers.Count(n => n == true);
            if(extendedFingerCount == 0)
            {
                rsp = RSP.Rock;
            }
            else if(extendedFingerCount < 4)
            {
                rsp = RSP.Scissors;

            }
            else
            {
                rsp = RSP.Paper;

            }

        }
	}
}
```

コレをシーン内の好きなオブジェクトにアタッチすればrspにグーチョキパーの認識結果が表示されます。
##解説
###usingステートメント
LeapMotionの機能を利用するには`using Leap;`のステートメントを利用します。
###Controller
LeapMotionの制御はControllerで行い、これがメインのインターフェースとなります。
###Frame
`Controller.Frame`には手の挙動の情報が含まれています。
###Hand
Hand型には手の位置、角度や各指の情報が存在しています。`Controller.Hands`で認識されている全ての手についてのHandを返します。認識範囲内に一つも手がなければ当然要素数0になるので例外処理をしっかりしましょう。
###Finger
各手についている指の情報を保有する型です。位置、指し示す向き、握っているか否かなどの情報を取得することができます。`Hand.Fingers`ですべての指のFingerを取得することができます。また、各Fingerに対して`Finger.IsExtend`で指の開きを検知することができます。指が開いているときTrue、指が曲がっているときがFalseです。上記の実装例では`isGripFingers = Array.ConvertAll(fingers, new Converter<Finger, bool>(i => i.IsExtended));`で全ての指の曲がり方のブール値を取得したのち、`int extendedFingerCount = isGripFingers.Count(n => n == true);`(LinQを利用しています)で曲がっている指の本数を取得しています。
###RSP
曲がった指の本数を基準にグーチョキパーのいずれの手が出されているか判断します。RSPは手の種類をまとめたEnumで、今回は曲がった指の本数が5本でグー、4~2本でチョキ、1本以下でパーとしています。
![キャプチャ164.PNG](https://qiita-image-store.s3.amazonaws.com/0/256231/84c54d91-6d72-8ae8-9961-5303a3f3c10d.png)
![キャプチャ165.PNG](https://qiita-image-store.s3.amazonaws.com/0/256231/4512141a-80a1-36eb-c5ad-fcd3927ebf40.png)
![キャプチャ163.PNG](https://qiita-image-store.s3.amazonaws.com/0/256231/38691ed1-024c-e952-2158-c166350fc65a.png)

##問題点
取り敢えず雑に認識させることができましたが、チョキとグーの認識が結構厳しく、ちゃんと手を下に向けないとうまく認識してくれなかったりします。これについては部屋が明るいのとか僕の手がオタク特有のまっしろハンドなのとかいろいろ原因がありそうです。
##解決のアイデア
まぁ別にガチじゃんけんするわけでもないしコンピュータ側の出す手に合わせてユーザーを勝たせる側に強めの認識補正をかけてあげればいいんじゃないでしょうか。勝てる手を出したのに認識ミスで負けるってのはプレイフィール最悪なのでそれだけは避ける方向で行くといいと思います。
#最後に
ちょうどVroidHubSDKも頂けたのでこれを利用して好みのモデルとじゃんけんが出来るシステムを組んでいきたいです。やるぞ～～