はじめに
マウスやキーボードが使えなくて、入力装置がLeapMotionしかない状況で、 Main Camera
を動かす方法を考えてみました。
デモ動画
実際に作ったのはこんな感じです。(表示しているのはLookingGlassですが、趣旨とは関係ないので説明を省きます。)
#LookingGlass 用のアプリを作成中。 #LeapMotion で取得した手の動きで、Unity内のカメラを動かしてみました。手をグーの形にしている間はカメラを動かせます。グーの形じゃなくなったら終了します。マウスドラッグみたいな感覚で使えるの便利です。更に、前後にも動かせます! pic.twitter.com/fE8lM754vW
— せぎゅ (@segur_vita) 2019年3月21日
手の形がグーのときは、手の動きに合わせてカメラを動かします。グーじゃないときは動かしません。
マウスドラッグの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】手の動きでカメラを動かす:両手編