Holographic Academy 211をやってみた

投稿者: | 2017-02-19

またもやHolographic Academyです。今回はジェスチャー操作のテクニックを紹介する211 Gestureをやってみました。今回も全体の解説ではなく、私の好奇心を刺激した狭いポイントの調査結果、のような感じの記事になりました。

日本語解説はRiftupさん(@WheetTweet)の記事がわかりやすくまとまってるので、そちらへGo Ahead。(またもや丸投げ)

InteractionManagerとGestureRecognizerは何が違うの?

InteractionManagerは指がセンサ内に入った、倒された、見失ったといったタイミングでイベントが発火されるのに対し、GestureRecognizerではタップされた、ホールド(ドラッグ操作に相当)されたといった、ある規定の動作がデバイスに認識されたタイミングでイベントが発火されます。InteractionManagerが低レイヤに指の状態を検出するのに対し、GestureRecognizerは高レイヤにジェスチャー(=特定の動き)を認識する、といったイメージでしょうか。

なお、InteractionManagerは非staticなクラスですが、イベントはすべてstaticです。

// デバイスが指を検出したときに発火する
InteractionManager.SourceDetected += InteractionManager_SourceDetected;
// デバイスが指を見失ったときに発火する
InteractionManager.SourceLost     += InteractionManager_SourceLost;
// 指が動いたことを検知したときに発火する
InteractionManager.SourceUpdated  += InteractionManager_SourceUpdated;
// 指が倒されたときに発火する
InteractionManager.SourcePressed  += InteractionManager_SourcePressed;
// 倒された指が持ち上げられたときに発火する
InteractionManager.SourceReleased += InteractionManager_SourceReleased; 

一方、GestureRecognizerを使うためにはインスタンス化の必要があります。また、SetRecognizableGesturesで認識したいジェスチャーを登録する必要があります。

GestureRecognizer recognizer = new GestureRecognizer();
// OR演算子で複数種類のジェスチャーを対象として登録することができます
// どのようなジェスチャーがあるのかは後述
recognizer.SetRecognizableGestures(GestureSettings.Tap | GestureSettings.ManipulationTranslate);

インスタンス化できるということは、一つのプログラム内で複数のGestureRecognizerを管理することができ、状況に応じて使い分けるといったことも可能になります。実際にChapter 4ではボイスコマンドを契機にGestureRecognizerを切り替えており、Navigation系かManipulation系か、認識対象のジェスチャーを切り替えています。GestureRecognizerが認識できるジェスチャーは似たような動作のものがあったり、同じ動作でも取得できるイベントデータが異なるジェスチャーも存在するので、GestureRecognizerの切り替えは地味に重要なテクニックなのかもしれません。

InteractionSourceStateにはどんな情報が入っているの?

InteractionManagerから発火される各種イベントには、イベントデータとしてInteractionSourceStateが一緒にハンドラへ送信されます。このイベントデータからどんな情報が取れるのか気になったので調べてみました。

Ray headRay
イベントが発火した時点のカメラからのRayです。directionはカメラの前方方向(オブジェクト空間の+z軸方向)を向いています。
bool pressed
指が倒されているかどうかのbool値です。倒されている場合はtrue、そうでない場合はfalseがセットされます。
InteractionSource source
InteractionManagerが検知したものについての情報が入っています。以下の2つのプロパティを持っています。

uint id
検知した指に対するIDがセットされています。指を新たに検出するたびに値がインクリメントされます。
InteractionSourceKind kind
検出したものが何かを表す値がセットされています。リファレンスによると、手や声、コントローラーを表す値がセットされるようですが、HoloLensでは手(実際には手ではなく指を検出しているように見えます)を表すHandが常にセットされます。HoloLensでは声をKeywordRecognizer等で処理しているのでInteractionMangerでは捕まえないのかもしれません。
InteractionSourceProperties properties
検出した指についての各種情報がセットされています。

InteractionSourceLocation location
検出した指の位置(おそらくワールド空間座標系)と速度がそれぞれVector3でセットされた構造体です
Vector3 sourceLossMitigationDirection
指が検出範囲の端付近に位置している場合、指をどの方向に動かせば検出範囲の中央に位置するのかを表すVector3がセットされます
double sourceLossRisk
検出している指がセンサーの検知範囲内のどのあたりに位置しているのか、0~1で表します。検知範囲外領域に近づけば近づくほど1に近い値がセットされます

Chapter 3ではsourceLossRiskを使って、この値がある一定値を超えると、指の位置を戻すようユーザにサジェスチョンしてくれる機能を実装しています。HW?ネイティブ?側でどのようにリスク値を計算しているのか気になるところです。

HoloLensでのGestureRecognizerはどんなジェスチャーを認識できるの?

HoloLensにおけるGestureRecognizerで認識できるジェスチャーは大別すると、Tap系とHold系になるかと思います。しかしながら、HoloLensではUnityのリファレンスにあるジェスチャーすべてが認識できるわけではないようです。

Tap系のジェスチャーとしてはTapとDoubleTapが値として用意されていますが、HoloLensで認識できるのはTapだけでした。

Hold系としてはManipulationTranslateとNavigation〇〇があります。これらはどちらもHoldした状態で指を動かすとイベントが発火し、Holdを開始した地点からどのくらい離れているかをイベントデータからVector3で取得できます。違いとしてはManipulationTranslateはx-y-z軸すべての移動量を取得できるのに対し、Navigation系は登録する値によって移動量を取得できる軸が限定されます。例えばSetRecognizableGesturesにGestureSetting.NavigationXを渡すと、イベントで取得できる移動量はx軸のものだけとなり、他の軸の移動量は0がセットされ取得することができません。

ManipulationTranslateとNavigationでは動作としては同じものであっても、イベントデータで取得できる値は変わってきます。すべてManipulationTranslateで取得して、スクリプト側でデータを加工して取り扱ってもよいかと思いますが、アプリの仕様に応じて設定するジェスチャーを選択してあげたほうが、コードの見通しはよくなると思います。

まとめ

前回に引き続きHolographic Academyについて、211 Gestureをやってみて思ったことを書き下してみました。カリキュラム順に進めようとすると、Voice→Spatial Sound→Spatial Mappingになるのですが、特に興味あるのが空間認識の部分になりますので、スキップしてSpatial Mappingに取り掛かってみようと思います。そこを押さえれば習作用のアプリが作れそうなので実装を開始したいと思ってます。