のしメモ アプリ開発ブログ

Unityアプリとかロボットとか作ってるときに困ったこととかメモ

HoloLens+Unity開発 入力の取得まとめ編

HoloLensが届きました!
こんな感じでVRで作っていた「Makebox」の移植をしてみました。Unityで開発できて、開発環境も揃っているので楽しいですよ!

今回は「HoloToolKit-Unityを使用した入力の取得方法」をまとめてみました。
これができるようになれば、後はUnityのコードをがりがり書くだけで色々な表現ができるようになるはずです。

HoloToolKitの導入はこちらを参照してください。

HoloToolkit-Unity/GettingStarted.md at master · Microsoft/HoloToolkit-Unity · GitHub

以下のオブジェクトがシーンに存在する状態で使用してください

HoloLensCamera.prefab
InputManager.prefab
DefaultCursor.prefab
EventSystem

紹介する機能

機能 モジュール名 コールバックのタイミング
入力 IInputHandler 対象へのInputDown / 対象へのInputUp
クリック IInputClickHandler 対象へのクリック
フォーカス IFocusable 対象とGazeが重なったとき / 対象からGazeが外れたとき
手の検出 ISourceStateHandler 対象を見ている状態で手が認識されているとき / 対象を見ている状態で手が認識から外れたとき
ホールド IHoldHandler 対象をつまみだした / 対象をつまんでいる / 対象をつまみ外した
ドラッグ(操作) IManipulationHandler 対象をつまみだした / 対象をつまんでいる / 対象をつまみ外した / 途中でトラッキングが外れた
ドラッグ(ナビゲーション) INavigationHandler 対象をつまみだした / 対象をつまんでいる / 対象をつまみ外した / 途中でトラッキングが外れた
音声の検出 ISpeechHandler 対象を見ながらしゃべった時のテキストを返す

※ IManipulationHandler、INavigationHandlerの違いについて
Manipulationは空間でものを動かしたり、何か書いたりしている基本的な動かすことに向いている。
Navigationはジョイスティックのようなもので、閾値(-1~1)も設定されているので、UIとかで使ったりするのに向いている。
詳細は下記
https://developer.microsoft.com/en-us/windows/holographic/Gestures.html#manipulation

※「対象」というのはスクリプトとコライダーがアタッチされているゲームオブジェクトを指します。
またSetGlobalListenerを同じオブジェクトにアタッチすることで、どこを見ていてもコライダーをつけずにイベントを発火することが可能です。

概要

それぞれのモジュールを継承することで、任意のコールバックが呼ばれるような仕組みとなっています。
Monobehaviourを継承したら、StartやUpdateが呼ばれるような感じで、Inputの際も定義したコールバックが呼ばれます。

使い方

InputModuleをusing

using HoloToolkit.Unity.InputModule;

使用するモジュールを継承(複数可)

public class InputTest : MonoBehaviour, IInputHandler, IInputClickHandler, IFocusable

コールバックを定義する

IInputHandler

public void OnInputUp(InputEventData eventData)
{
    Debug.LogFormat("OnInputUp\r\nSource: {0}  SourceId: {1}", eventData.InputSource, eventData.SourceId);
}

public void OnInputDown(InputEventData eventData)
{
    Debug.LogFormat("OnInputDown\r\nSource: {0}  SourceId: {1}", eventData.InputSource, eventData.SourceId);
}
IInputClickHandler

public void OnInputClicked(InputClickedEventData eventData)
{
    Debug.LogFormat("OnInputClicked\r\nSource: {0}  SourceId: {1} TapCount: {2}", eventData.InputSource, eventData.SourceId, eventData.TapCount);
}
IFocusable

public void OnFocusEnter()
{
    Debug.Log("OnFocusEnter");
}

public void OnFocusExit()
{
    Debug.Log("OnFocusExit");
}

ISourceStateHandler

public void OnSourceDetected(SourceStateEventData eventData)
{
    Debug.LogFormat("OnSourceDetected\r\nSource: {0}  SourceId: {1}", eventData.InputSource, eventData.SourceId);
}

public void OnSourceLost(SourceStateEventData eventData)
{
    Debug.LogFormat("OnSourceLost\r\nSource: {0}  SourceId: {1}", eventData.InputSource, eventData.SourceId);
}

手の検知はあんまり知られてなさそう。仕組み的に指さし->パーの検知ができる。ちょっと何かに使えるかも?

IHoldHandler

public void OnHoldStarted(HoldEventData eventData)
{
    Debug.LogFormat("OnHoldStarted\r\nSource: {0}  SourceId: {1}", eventData.InputSource, eventData.SourceId);
}

public void OnHoldCompleted(HoldEventData eventData)
{
   Debug.LogFormat("OnHoldCompleted\r\nSource: {0}  SourceId: {1}", eventData.InputSource, eventData.SourceId);
}

public void OnHoldCanceled(HoldEventData eventData)
{
   Debug.LogFormat("OnHoldCanceled\r\nSource: {0}  SourceId: {1}", eventData.InputSource, eventData.SourceId);
}

IManipulationHandler

public void OnManipulationStarted(ManipulationEventData eventData)
{
    Debug.LogFormat("OnManipulationStarted\r\nSource: {0}  SourceId: {1}\r\nCumulativeDelta: {2} {3} {4}",
        eventData.InputSource,
        eventData.SourceId,
        eventData.CumulativeDelta.x,
        eventData.CumulativeDelta.y,
        eventData.CumulativeDelta.z);
}

public void OnManipulationUpdated(ManipulationEventData eventData)
{
    Debug.LogFormat("OnManipulationUpdated\r\nSource: {0}  SourceId: {1}\r\nCumulativeDelta: {2} {3} {4}",
        eventData.InputSource,
        eventData.SourceId,
        eventData.CumulativeDelta.x,
        eventData.CumulativeDelta.y,
        eventData.CumulativeDelta.z);
}

public void OnManipulationCompleted(ManipulationEventData eventData)
{
    Debug.LogFormat("OnManipulationCompleted\r\nSource: {0}  SourceId: {1}\r\nCumulativeDelta: {2} {3} {4}",
        eventData.InputSource,
        eventData.SourceId,
        eventData.CumulativeDelta.x,
        eventData.CumulativeDelta.y,
        eventData.CumulativeDelta.z);
}

public void OnManipulationCanceled(ManipulationEventData eventData)
{
    Debug.LogFormat("OnManipulationCanceled\r\nSource: {0}  SourceId: {1}\r\nCumulativeDelta: {2} {3} {4}",
        eventData.InputSource,
        eventData.SourceId,
        eventData.CumulativeDelta.x,
        eventData.CumulativeDelta.y,
        eventData.CumulativeDelta.z);
}

INavigationHandler

public void OnNavigationStarted(NavigationEventData eventData)
{
    Debug.LogFormat("OnNavigationStarted\r\nSource: {0}  SourceId: {1}\r\nCumulativeDelta: {2} {3} {4}",
        eventData.InputSource,
        eventData.SourceId,
        eventData.CumulativeDelta.x,
        eventData.CumulativeDelta.y,
        eventData.CumulativeDelta.z);
}

public void OnNavigationUpdated(NavigationEventData eventData)
{
    Debug.LogFormat("OnNavigationUpdated\r\nSource: {0}  SourceId: {1}\r\nCumulativeDelta: {2} {3} {4}",
        eventData.InputSource,
        eventData.SourceId,
        eventData.CumulativeDelta.x,
        eventData.CumulativeDelta.y,
        eventData.CumulativeDelta.z);
}

public void OnNavigationCompleted(NavigationEventData eventData)
{
    Debug.LogFormat("OnNavigationCompleted\r\nSource: {0}  SourceId: {1}\r\nCumulativeDelta: {2} {3} {4}",
        eventData.InputSource,
        eventData.SourceId,
        eventData.CumulativeDelta.x,
        eventData.CumulativeDelta.y,
        eventData.CumulativeDelta.z);
}

public void OnNavigationCanceled(NavigationEventData eventData)
{
    Debug.LogFormat("OnNavigationCanceled\r\nSource: {0}  SourceId: {1}\r\nCumulativeDelta: {2} {3} {4}",
        eventData.InputSource,
        eventData.SourceId,
        eventData.CumulativeDelta.x,
        eventData.CumulativeDelta.y,
        eventData.CumulativeDelta.z);
}

ISpeechHandler


使用される単語をどれか一つのオブジェクトにあらかじめ登録しておく必要があります。
SpeechInputSourceを任意のオブジェクトにアタッチし、単語を登録する。
f:id:noshipu:20170128041538p:plain

public void OnSpeechKeywordRecognized(SpeechKeywordRecognizedEventData eventData)
{
    Debug.Log(eventData.RecognizedText.ToLower());
}

最後に

これでインタラクションコンテンツが作れるようになるのではないでしょうか。
また、間違った記載がありましたら、コメントやTwitter等でご指摘ください。