LeapMotionを使って空中に絵を描く方法



#はじめに#
**LeapMotion**を手に入れたので、せっかくなので[Tilt Brush](https://store.steampowered.com/app/327140/Tilt_Brush/?l=japanese)のような空中に絵が描けるアプリケーションを作ってみようと思い立ちました。
こんな感じのものができました

![Fz2o2RWW9fOO9VoW_Trim.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/517673/af85a305-bf62-1123-8582-ea07bf57a821.gif)



<font color="silver">最近、オープンソースになりましたね。</font>
それではさっそくやっていきましょう！

#LeapMotionをunityで動かすまで#
まずは公式のチュートリアルを見たりググったりしてLeapMotionのセットアップを終わらせましょう。
~~セットアップが終わってもチュートリアル画面が一切出なかったのは私だけですか~~
次に[このページ](https://atl-hiroo.recruit-tech.co.jp/2018/01/unity_leap-motion/)をもとにunityにCoreというアセットを導入します。
ここまで来たらとりあえずサンプルを実行してみましょう。
Assets>Plugins>LeapMotion>Core>Examplesにたくさんサンプルがあります。
~~正直それぞれのサンプル間の違いはよく分かりません~~

さてサンプルまで動かせたところで本題に入っていきます。

#LeapMotionで空中に絵を描く#
すでに手のモデルが動かせる状態のCapsule Hands(Desktop)を改造していきます。
いきなりプログラムを書く前に空中に絵を描けるようになるまでに必要なステップを洗い出します。

1. 指先の座標を取得する
2. 取得した座標にインクを乗せる

この2つが達成できればとりあえず目的は果たせそうです。

##1.指先の座標を取得する##
こちらは簡単です。
人差し指の先をペンに見立てたいので、HandModels>RigidRoundHand_R>index>bone3をそのままペン先の座標として取得してしまいます。
他の指をペン先にしたい場合はそっちの指のboneの座標を取得してください。

##2. 取得した座標にインクを乗せる##
問題はこちらです。線を描画するComponentとしてまず思いつくのは**Line Renderer**です。
ということでこれを使って2を実現していきます。
まずはLineRendereを生成するprefabを作ります。
空のゲームオブジェクトを生成して、Line Rendererを追加して名前をLineObjectとしてprefab化します。
次にこれをインスタンス化して絵を描くためのスクリプトを書きます。

````cs:Paint.cs
using System.Colle:ctions;
using System.Collections.Generic;
using UnityEngine;
using Leap;
using Leap.Unity;


public class Paint : MonoBehaviour
{
    [SerializeField] GameObject LineObjectPrefab;
    Transform myTrans;
    Vector3 tipPos;

    private GameObject CurrentLineObject = null;

    private void Update()
    {
        myTrans = this.transform;
        tipPos = myTrans.position;

        //スペースキーを押した瞬間
        if (Input.GetKeyDown(KeyCode.Space)) CurrentLineObject = Instantiate(LineObjectPrefab, new Vector3(0, 0, 0), Quaternion.identity);


        //スペースキーを押してる間
        if (Input.GetKey(KeyCode.Space))
        {
      
            //ゲームオブジェクトからLineRendererコンポーネントを取得
            LineRenderer render = CurrentLineObject.GetComponent<LineRenderer>();

            //LineRendererからPositionsのサイズを取得
            int NextPositionIndex = render.positionCount;

            //LineRendererのPositionsのサイズを増やす
            render.positionCount = NextPositionIndex + 1;

            //LineRendererのPositionsに現在の指先の位置情報を追加
            render.SetPosition(NextPositionIndex, tipPos);
            Debug.Log(tipPos.x);
        }

        //スペースキーを押してないとき
        else //スペースキーから指を離したとき
        {
            if (CurrentLineObject != null)
            {
                //現在描画中の線があったらnullにして次の線を描けるようにする。
                CurrentLineObject = null;
            }
        }
    

    }
  
}
```

毎フレームインクが描画されてしまうと困るため、スペースキーを押している間だけインクが描画されるようにしています。
さてこのスクリプトを、右手の人差し指のbone3にアタッチして先ほど作ったLineObj
ectをアタッチします。
恐る恐る実行ボタンを押してみると、スペースキーを押している間、指先からインクが描画されてました。やったね！
とりあえず当初の目的は達成されました。
しかしインクの色がdefault materialなので地味な灰色にしかなりません。
次にインクの色を変えられるようにしてみましょう。

##3. インクの色を変える##
インクの色を変えるために新しくChangeMaterialColorというスクリプトを作りLineObjectにアタッチします。
そして以下のようなコードを書きます。

````cs:ChangeMaterialColor.cs

using UnityEngine;
public class ChangeMaterialColor : MonoBehaviour
{
   public float r, g, b;
  [SerializeField] Material material;
 
    private void Update()
    {
        material = GetComponent<Renderer>().material;
        material.SetColor("_Color", new Color(r,g,b));

        //削除
        if (Input.GetKeyDown(KeyCode.Delete)) Destroy(gameObject);
      
    }
}

```
書き間違えた時に線を消せるような機能も入れています。
そして新しくマテリアルを作ってLineRendererのMaterialsと今さっき書いたスクリプトの両方にアタッチしています。
R,G,B成分を設定することによってペンを任意の色に変えることができます。
(本当はカラーピッカーを使って色を指定したかったんですがやり方がよくわからなかったので誰かわかる方がいらっしゃったら教えてください。)

#おわりに#
インクの色を変えられた後、カメラも動かせるようにしたんですが、あまりうまく制御できていないので割愛します。
実は掲載しているコードは実はフルスクラッチではなく、ネット上にあったoculusQuestとコントローラで空中に絵が描けるものの解説記事をLeapMotion仕様に書き換えて色々と手を加えたものなんですが、その参考にした記事が（おそらくGoogle君の検索アルゴリズムのせいで）見つかりませんでした。元記事様がなかったらこれは作れていませんでした。もしどの記事か心当たりがある方は教えてくれると助かります。
~~LeapMotionの記事の本数はリリース周辺と2018年前後を除いて記事の本数が少ないけど大丈夫なのかな。最近特にLeapMotion界隈が下火になっていそうな気がする~~

質問コメント訂正お気軽にどうぞ。