つくるの大好き。

つくるのが大好きな人の記録。

MRTKのAppBarで簡単視野追従フローティングメニューを作ってみた

HoloLensアプリを作っていると、ちょっとしたシンプルなメニューを呼び出して処理を選択したいということがよくあります。
そしてそれがあまり頻繁には使わない機能だったり、見栄えをスッキリさせたいという場合、使わないときはメニューを非表示にしておきたいですよね。
でもエアタップを他のアクションに割り当ててしまっている場合、HoloLensにはマウスの右クリックに相当するジェスチャーがないのでどのようにしてメニューを呼び出すか悩んでしまいますよね。
そこでこんなUIを作ってみました。


FloatingAppBar

  • 空間を数秒ホールドするとフローティングメニュー(AppBar)が現れる
  • メニューは視線を移動しても視野内に追従して付いてくる
  • 項目はフォーカス時、選択時に効果音を鳴らし操作感を上げる

このメニューを適用したプロジェクトはGitHubで公開しています。
エアタップはUnityちゃんの呼び出しに割り当ててあり、フローティングメニューでSpatialMapping表示有無の切り替え、BGMのミュート/ミュート解除を用意しています。

github.com

AppBarの表示、アクションの設定

メニューはAppBarを利用します。
AppBarを利用することで簡単にボタンが並んだメニューを作ることができます。
ボタンのアクションはInteractiveReceiverにリンクさせることで一つのコンポーネントで集中管理することができます。

f:id:peugeot-106-s16:20180528220752p:plain

  • SquareButtonPrefab
    指定先のPrefabでボタンの表示などをカスタマイズできます。今回はアイコンなし、テキストを中央表示にしたPrefabを指定しています
  • Use XXX
    デフォルトで用意されている Remove Adjust Hide ボタンを非表示にします
  • Display Type
    独立型のAppBarとします

f:id:peugeot-106-s16:20180528221642p:plain

  • Type
    Customとします
  • Name ボタン名、アクションを実際に処理するInteractiveReceiverのコードでアクション種別の識別に利用される識別子です
  • Text
    ボタンに表示されるテキスト
  • EventTarget
    アクションを実際に処理するInteractiveReceiverの派生コンポーネントを指定します

AppBarの自動移動の設定

視線を移動した際にメニューが視野内に自動追従してくる機能はSolver関連のRadial Viewコンポーネントを利用することで簡単に実現できます。

f:id:peugeot-106-s16:20180528222002p:plain

カスタマイズポイントとしては、追従時に視野のどの程度の範囲内に留まるかの指定があります。
デフォルトではHoloLensの場合ほとんど視野に入ってこないので小さめにしています。
ただImmersive Headsetで見た場合はかなり狭い範囲に収まってしまって逆に不自然だったので双方で不自然でないギリギリの設定をしました。 ここはもう少し丁寧に作りこむ場合、プラットフォームによって設定値を変えた方が良いと思います。

  • Max View Degrees
    オブジェクトが留まる視野内の範囲を度数で指定します
  • Aspect V
    オブジェクトが留まる範囲の縦横比。横1.0に対する縦の比率。

InteractiveReveiverでのアクション処理

ボタンのタップ処理は指定したInteractiveReceiverに通知され、ボタンのNameを識別子とすることで下記のように振り分け処理を行えます。

HoloLensUnityChan/Control.cs at b4c824c82efffd2b68f77ca3e4e74acc61f2fe8d · SystemFriend/HoloLensUnityChan · GitHub

    protected override void InputClicked(GameObject obj, InputClickedEventData eventData)
    {
        switch (obj.name)
        {
            case "Close":
                this.appBar.gameObject.SetActive(false);
                break;
            case "Mapping":
                this.IsDrawSpatialMappingWireframe = !this.IsDrawSpatialMappingWireframe;
                this.spatialMappingManager.SurfaceMaterial = this.IsDrawSpatialMappingWireframe ? this.spatialMappingMaterialWireframe : this.spatialMappingMaterialOcclusion;
                break;
            case "BGM":
                this.audioSource.volume = (audioSource.volume > 0f) ? 0f : 0.5f;
                break;
            default:
                base.InputClicked(obj, eventData);
                break;
        }
    }

Profileによるボタンの体裁や効果音の変更

Profileを編集することで一括してUIの体裁などを変更することができます。
MRTKにデフォルトで用意されているProfileを変更してしまうと、MRTKのアップデートなどでファイルを上書きした際に設定が戻ってしまうので、コピーを作成しそこに独自の設定を行うことをお勧めします。 また、下記画像で示しているProfileを参照しているHolographicButton Prefab も同様に直接変更せずコピーを作成して編集することをお勧めします。
ここでは独自のProfileを参照することと、アイコン表示をdisableとしています。

f:id:peugeot-106-s16:20180528224823p:plain

ボタンの体裁は AppButtonTextProfile にてテキストを中央揃えとしました。

f:id:peugeot-106-s16:20180528225228p:plain

効果音は AppButtonSoundsProfile にてクリック時の音とフォーカス移動時の音を独自のものに変更しています。

f:id:peugeot-106-s16:20180528225325p:plain

ホールドでのメニュー呼び出し

最後に空間ホールドでのメニュー呼び出しです。
これは、InputManagerにGlobalListnerにハンドラを追加することで実現します。

HoloLensUnityChan/GlobalInputHandler.cs at master · SystemFriend/HoloLensUnityChan · GitHub

public class GlobalInputHandler : MonoBehaviour, IHoldHandler, IInputClickHandler
{
    void Start ()
    {
        InputManager.Instance.AddGlobalListener(this.gameObject);
    }

    void IHoldHandler.OnHoldCompleted(HoldEventData eventData)
    {
        this.controller.appBar.gameObject.SetActive(true);
    }

まとめ

以上で簡単に視野追従型フローティングメニューをアプリに組み込むことができます。

f:id:peugeot-106-s16:20180528231547p:plain