自習室

こもります

HoloToolkit-Unity の InputManager の使い方

はじめに

HoloLens を入手して楽しそうなアプリを一通り試して、Academy をやってみたりしてそろそろ自分でアプリを作ってみようかなというところなのですが、ちょっと一息ついて便利な HoloToolkit-Unity 様を調査してみようと思いました。

Academy では、3分クッキング的と言うか「これとこれを足して、ここだけ手作業ね☆」 みたいな感じであまり頭に入らなかった + HoloToolkit-Unity はAcademy でやっていることをほぼラップして使いやすくしてくれているようだったので、HoloToolkit-Unity を実際に触りながら多少試行錯誤的に調べてみた内容をまとめてみます。

HoloLensCamera の次に、HoloLens開発をしていたら間違いなく使うだろう、 InputManager について、本記事ではまとめたいと思います。

InputManager の概要

ソースコードInput/Readme.md に、 InputManager の概説がまとめてあります。 基本的なとこを抜き出すと

github.com

Scripts that leverage HoloLens input features namely Gaze, Gesture and Voice.
This contains a fully-featured input module, which allows you to handle various types of input and send them to any game object being currently gazed at, or any fallback object. It also includes a cursor similar to the HoloLens shell cursor that fully leverages the Unity’s animation system.

HoloLens のインプット機能(すなわち Gaze, Gesture, Voice) を活用するためのスクリプト
各種インプットをハンドルして、注視しているオブジェクトに対しそれらインプットを送ります。(注視対象がない場合はフォールバックのターゲットに送ります) また、HoloLens標準と似た挙動をするカーソルも用意しています。

InputManager を使うと、視線、ジェスチャ、音声コマンドを使えるようになるし、視線カーソルも使えるようになる、ってことですね。なんて素敵!

内容

多くの部分は先人達が探索して下さってますので、この記事ではそのまとめをしつつ、説明があまりなかった辺りを調査してみた結果をまとめます。

  • HoloToolkit-Unity の基本的な使い方
  • InputManager の基本的な使い方
  • InputManager の使い方の少しだけ詳細
  • HandDraggable の使い方
  • SpeechInputManager の使い方
  • InputManager が他にやっていること

HoloToolkit-Unity の基本的な使い方

大前提として。 @kaorun55 大先生がまとめてくださっています。間違いなし。

docs.com

InputManager の基本的な使い方

@noshipu さんがまとめてくださっています。こちらの記事では、InputManager がさばくイベントのうち、 FocusedObjects に対するイベントについてに説明されています。視線を向けた対象にタップや音声コマンドなどを紐付ける方法についてです。

noshipu.hateblo.jp

InputManager のやっていること少しだけ詳細

多くの場合は、@noshipu さんのまとめて下さっている情報で足りると思うのですが、せっかくなのでもう少し InputManager.cs を読んでみて、中でやっていることをざっくりと調べてみました。

InputManager が管理するインプットのハンドラは大まかに5種類
GlobalListeners いかなるときも呼ばれる。カーソルの状態を切り替えたりするのに使われている
ModalInputStack FocusedObjectsに先んじて呼ばれる。ある特定のモード(例えばドラッグ中)に入っているときに処理される
FocusedObjects 上記で @noshipu さんが説明してくださっている内容。専用のinterfaceを実装することで、ゲームオブジェクトを注視した状態でクリックや音声コマンドなどを入れることができる
UnityUIPointerEvent uGUIで消費する。uGUIを置くだけで使えるので便利
FallbackInputStack ModalにもFocusedObjectsにもUnityUIにも引っかからなかった場合に呼ばれる。どんな場所でも何かしら特定のインタラクションが起きたこと自体を拾いたい場合に使う
GloblListeners の使い方

ざっくり読んでみたところ、HoloToolkit-Unityとして提供されている中では、GlobalListeners はカーソルの状態制御にのみ利用されています。(オブジェクトの注視状態に関わらず)手の上げ下げというインプットを受けてカーソルの状態を変えたりしています。

それ以外の使い出としては、たとえば 「リスナーを備えた対象物があってもなくてもとにかくタップした回数を数えたい」 みたいなときには、以下の様なスクリプトを作って、空のオブジェクトに貼り付けると可能です。

using UnityEngine;
using HoloToolkit.Unity.InputModule;

namespace HoloToolkit.Unity
{
    public class GlobalInputListener : Singleton<GlobalInputListener>, IInputClickHandler
    {
        public void OnInputClicked(InputClickedEventData eventData)
        {
            Debug.Log("Call Global Listener");
            // ここでなんか数字をカウントアップしたり。
        }

        void Start()
        {
            InputManager.Instance.AddGlobalListener(gameObject);
        }
    }
}

GlobalListener の解除用に、 InputManager.Instance.RemoveGlobalListener() と言う関数も用意されています。

ModalInputStack の使い方

ModalInputStackHandDraggable.cs 中でドラッグでの consume にのみ使われているようでした。 HandDraggable.cs をちょっと改良すると、つまんでドラッグする事で物体を回転させるスクリプトも簡単に書けそうです。そんなときに ModalInputStack が使えるでしょう。

HoloToolkit-Unity/HandDraggable.cs at master · Microsoft/HoloToolkit-Unity · GitHub

ModalInputStack へのリスナーの登録/解除関数は以下です。

  • 一つ追加: InputManager.Instance.Push()
  • 一つ解除: InputManager.Instance.Pop()
  • 全部解除: InputManager.Instance.Clear()

ModalInput を利用するには、そのモードへの入り・終わりを定義する必要があるので、上記の追加・解除関数をモードの入り終わりに呼ぶ事になります。その辺は HandDraggable.cs を読むと何となく分かります。

UnityUI の使い方

通常通り GUIを作って、WorldSpace にするだけでOKです。特定の interface 等の実装は必要ありません。

そういえば、HoloLens で OnScreen な GUI を作るのってできないっすね。(カーソル = ディスプレイ中心、なので)

FallbackInputStack の使い方

FocusedObjects のように実体があって collision を持つゲームオブジェクトが、タップ等のイベントを consume しなかった場合、FallbackInputStack にハンドラを登録しておくことで、そいつが受け取ってくれるようになります。フォールバックですね。

InputManager には PushFallbackInputHandler という、フォールバックのハンドラを登録するための関数が用意されているのでそれを用います。たとえば下記のようなスクリプトを用意して、空のオブジェクトにアタッチしておくと、@noshipu さんの記事で扱った手順で登録されたゲームオブジェクトのいずれにも視線を向けずに (つまり空振りの) タップしたときに限り、「fallback input」とログが出ます。

using UnityEngine;
using HoloToolkit.Unity.InputModule;

namespace HoloToolkit.Unity
{
    public class FallbackInputManager : Singleton<FallbackInputManager>, IInputClickHandler
    {
        public void OnInputClicked(InputClickedEventData eventData)
        {
            Debug.Log("fallback input");
            // はずれ!って表示したりとか。
        }

        void Start()
        {
            InputManager.Instance.PushFallbackInputHandler(gameObject);
        }
    }
}

HandDraggable の使い方

詳しくはこちらの記事で説明されています。オブジェクトに HandDraggable.cs をくっつけたら、手でつかめます、以上。

matatabi-ux.hateblo.jp

SpeechInputManager の使い方

FocusedObjects で消費できるインプットイベントのうち、speech に関しては、まずそれが認識対象のワードである(意図のある入力操作である)、ということを事前に登録していないことには、HoloLensも認識のしようがありません。それを司るのが SpeechInputManager.cs です。使い方は先程の @noshipu さんの記事にあります。

KeywordManager との住み分け

SpeechInputManager + ISpeechHandler と似た機能を提供する KeywordManager というのがいます。 KeywordManager の使い方に関しては、こちらの記事が詳しく説明してくださっています

zuq9nn.blogspot.jp

思想の違い、って感じなのですが、 SpeechInputManager + ISpeechHandler では、ISpeechHandler を実装した スクリプトをゲームオブジェクトにアタッチすることで、そのオブジェクトを注視している際にイベントを発行(音声コマンドを言う)すると、そのゲームオブジェクトが音声に応じて反応させることができるのに対し、KeywordManager の場合、gazeとは関係なく、音声コマンドと物や処理を一対一で対応付けていくことができるようになっています。

InputManager が他にやっていること

InputManager.prefab には、(細かいことをしない限りは)我々が直接触らないいくつかのスクリプトがアタッチされていますが、ざっくりと以下の様な感じになっています

f:id:AMANE:20170419235801p:plain

InputManager にアタッチされているスクリプト

GazeManager 結構重要。視線に関して色々情報がとれる。後述
GazeStabilizer 視線情報のスタビライズ
StabilizationPlaneModifier ここがよく分からないのですが、空間中に平面を仮定してその上で視線座標を動かすことで、視線をスタイビライズするような事をしている、、のかな。このページにそれっぽいことが書いてある

InputManager の子

RawInteractionSource 手の検出などプリミティブなイベントの検出をし、 InputManager に対してイベントを上げる
EditorHandsInput Editor実行時にジェスチャをエミュレートする。個人的にはあまり使い出がないと感じている
GesturesInput HoloLens の基本API群である UnityEngine.VR.WSA.Input に含まれる GestureRecognizer のラッパで、InputManager に対して各種 input イベントを上げてくる
GazeManager の便利メンバ
  • GazeManager.Instance.HitObject - 視線が当たっているゲームオブジェクトへの参照
  • GazeManager.Instance.HitPosition - 視線が当たっている衝突点の座標
  • GazeManager.Instance.HitInfo - 上二つを含む、RaycastHit 情報全部
  • GazeManager.Instance.IsGazingAtObject - Raycast 対象にしたレイヤー中に含まれて居る物体に当たっているか否か。
  • GazeManager.Instance.GazeOrigin - 起動時を原点とした視点(頭部)位置
  • GazeManager.Instance.GazeNormal - 視線方向

あたり。使える。

最後に

便利に使わせていただこうと思います。

初めに挙げた Input/Readme.md 中に、以下の様な記述があって、今後どのように WindowsHolographic もとい、Windows Mixed Reality が進化していくのかわくわくします。

and new input sources can be created to support different input devices.
The focus can be triggered by the user’s gaze or any other gaze source.
Currently, that gaze is always coming from the GazeManager class, but this could be extended to support multiple gaze sources if the need arises.