LoginSignup
4
4

More than 5 years have passed since last update.

【LeapMotion+UniRx】手の動きでカメラを動かす

Last updated at Posted at 2019-03-21

leap-motion-unirx-capture.png

はじめに

マウスやキーボードが使えなくて、入力装置がLeapMotionしかない状況で、 Main Camera を動かす方法を考えてみました。

デモ動画

実際に作ったのはこんな感じです。(表示しているのはLookingGlassですが、趣旨とは関係ないので説明を省きます。)

手の形がグーのときは、手の動きに合わせてカメラを動かします。グーじゃないときは動かしません。
マウスドラッグの3D版みたいな感じです。手前や奥にも動かせます。

サンプルコード

こんな感じのコードを作りました。このコードをカメラのオブジェクトにアタッチすれば、動くはずです。 UniRx を使っています。

using Leap;
using System.Collections.Generic;
using System.Linq;
using UniRx;
using UniRx.Triggers;
using UnityEngine;

/// <summary>
/// カメラ制御
/// </summary>
public class CameraController : MonoBehaviour
{
    /** カメラの移動スピード */
    private float speed = 0.025f;

    /** LeapMotionのコントローラー */
    private Controller controller;

    /** エントリポイント */
    void Start()
    {
        // LeapMotionのコントローラー
        controller = new Controller();

        // LeapMotionから手の情報を取得
        var handsStream = this.UpdateAsObservable()
            .Select(_ => controller.Frame().Hands);

        // グー終了判定ストリーム
        var endRockGripStream = handsStream
            .Where(hands => !IsRockGrip(hands));

        // カメラ制御
        handsStream
            // グーなら
            .Where(hands => IsRockGrip(hands))
            // 手のひらの位置を取得
            .Select(hands => ToVector3(hands[0].PalmPosition))
            // バッファに前回と今回の2つの値を詰める
            .Buffer(2, 1)
            // 今回と前回の差から手のひらの移動ベクトルを計算
            .Select(positions => positions[1] - positions[0])
            // 値をログに出力
            .Do(movement => Debug.Log("移動: " + movement))
            // グーが終了したらバッファをクリアにする
            .TakeUntil(endRockGripStream).RepeatUntilDestroy(this)
            // カメラを移動
            .Subscribe(movement => transform.Translate(-speed * movement));
    }

    /** グーかどうか */
    bool IsRockGrip(List<Hand> hands)
    {
        return
            // 片手なら
            hands.Count == 1 &&
            // 全ての指の内、開いている数が0個なら
            hands[0].Fingers.ToArray().Count(x => x.IsExtended) == 0;
    }

    /** LeapのVectorからUnityのVector3に変換 */
    Vector3 ToVector3(Vector v)
    {
        return new Vector3(v.x, v.y, -v.z);
    }
}

UpdateAsObservable について

Update() をストリームに変換したものです。

こちらの記事が丁寧でとても参考になります。
UniRx入門 その4 -Updateをストリームに変換する方法とメリット-

IsRockGrip について

ここで、手が グー の形かどうか判定しています。
まず、 hands.Count で、LeapMotionが検出している手の数が1つかどうかを確認しています。
そして、 hands[0].Fingers.ToArray().Count(x => x.IsExtended) で、開いている指の数を数えています。
この数が0ならじゃんけんの グー だと判断します。

この手法はこちらの記事を参考にさせていただきました。
LeapMotion+Unityでグー・チョキ・パーを認識する

ToVector3 について

  • LeapMotionから得られる座標値は右手系
  • Unityの世界座標は左手系

という違いがあったので、z座標の正負を反転させています。

TakeUntil について

手がグーじゃなくなったときにバッファを破棄するために、 TakeUntil を使っています。
この処理をしないと、グーじゃなくなった直前の手の位置がバッファに保持され、次に手をグーにしたときに、一気にカメラが動きます。

こちらの記事を参考にさせていただきました。
【UniRx】ピンチイン・アウトをちゃちゃっと作る

さいごに

両手を使えばピンチイン・アウトとかもできそうですね!やってみようと思います。

2019年4月22日追記
両手編を書きました!
【LeapMotion+UniRx】手の動きでカメラを動かす:両手編

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4