【MRDesignLab】ButtonHolographicを使おう

投稿者: | 2017-06-28

前回はMRDesignLabを使ってジェスチャー入力を取り扱う方法について解説しましたが、今回はMRDesignLabで提供されているUIコンポーネントであるボタンの基本的な仕組みについて解説したいと思います。

同梱されているサンプルプロジェクト(DesignLabs_Unity_Example)のInteractableObject_Exampleシーンに様々なタイプのボタンがあり、今回はその中でもシンプルなものであろうButtonHolographicをじっくり見ていきたいと思います。

概要

MRDesignLab内のUI系のコンポーネントは、Microsoftが提唱するFluent Design Systemを意識しているようで、ボタンをフォーカスすると光ったり、AirTapすると大きさが変化したり音が鳴るなどと、ユーザが触れたときに様々なインタラクションをするようになっています。なお、基本的なインタラクションはすべてUnityエディタ上で設定できるようになっております。

ButtonHolographicにアタッチされているスクリプトは以下のようになります。

オリジナルのボタンを作りたい場合は、礎となるCompoundButtonをアタッチし、必要に応じて振る舞いを拡張するスクリプトを追加していくのが良いと思います。

CompoundButton

CompoundButtonはボタンそのものを表すクラスであり、その基底クラスであるButtonにはボタンの状態や、前回説明したInteractionManagerからのSendMessgaeを受けて、イベントを発火する仕組みを備えています。

状態

Buttonには以下の6つの状態が定義されています。

Pressed
ボタンにフォーカスが当たっており、かつボタンをホールドしている。ボタンをAirTapしている最中。
Targeted
ボタンにフォーカスが当たっており、立ち上げた指が検出されている
Interactive
ボタンにフォーカスは当たっていないが、立ち上げた指が検出されている
ObservationTargeted
ボタンにフォーカスが当たっているが、指は検出されていない。
Observation
ボタンにフォーカスが当たっておらず、指も検出されていない
Disabled
ボタンが非活性状態である

イベント

Buttonから発火するイベントは以下の5つです。デリゲートの引数のGameObjectはButton自身のGameObjectがセットされます。

Action<GameObject> OnButtonPressed
ボタンがAirTap等で押下されたフレームにて発火する。
Action<GameObject> OnButtonHeld
ボタンがホールドされている間、発火する。
Action<GameObject> OnButtonReleased
ボタンが離されたフレームにて発火する
Action<GameObject> OnButtonCancelled
ボタンをホールド中に指をロストした場合に発火する。

以下のイベントのみデリゲートの引数が違います。
ButtonStateEnumは先ほど説明した6つのボタン状態の列挙体です。

Action<ButtonStateEnum> StateChange
ボタンの状態が変化したときに発火する。デリゲートの引数には新しいボタン状態がセットされる。

ボタンを押したときのアクションを実装する

上記のButtonから発せられるイベントをサブスクライブする方法がとてもシンプルです。

using HUX.Buttons;
using UnityEngine;

public class ButtonAction : MonoBehaviour {

    private void OnEnable() {
        var button = GetComponent<CompoundButton>();
        button.OnButtonReleased += ButtonReleased;
    }

    private void OnDisable() {
        var button = GetComponent<CompoundButton>();
        button.OnButtonReleased -= ButtonReleased;
    }

    private void ButtonReleased(GameObject obj) {
        Debug.Log("Button is tapped!");
        // ここにボタンが押されたときのアクションを実装する。
    }
}

また、下のイメージのように、押した結果、同じ動作をするボタンがシーン上に複数ある場合はInteractionReceiverを使うのも有効です。(InteractionReceiverの動作は前回記事を参照ください)

まずは以下のスクリプトを用意し、シーン上に配置した適当なGameObjectにアタッチします。

using HUX.Interaction;
using HUX.Receivers;
using UnityEngine;

public class SearchReceiver : InteractionReceiver {
    protected override void OnTapped(GameObject obj, InteractionManager.InteractionEventArgs eventArgs) {
        base.OnTapped(obj, eventArgs);
        Debug.LogFormat("{0} button tapped", obj.name);
        // ここにボタンがTapされたときのアクションを実装する
    }
}

続いて、Unityエディタ上で上記コードで定義したアクションを行うボタンを指定します。

この状態で各オブジェクト配下のSearchボタンをタップすると実装したアクションが動作します。

振る舞いを拡張するスクリプト

ButtonHolographicのオブジェクト構成

各スクリプトがどのように作用していくかを確認していく前に、まずはこのボタンオブジェクト自体がどのような要素で構成されているのかを見てみましょう。

各スクリプトはこれらの要素に作用する形でユーザに見せる振る舞いを作っています。たとえば、Gazeでボタンにフォーカスを当てると表面(UIButtonSquareFace)のマテリアルカラーを変化させる、AirTapをするとUIButtonSquareのScaleを縮小して押し潰したかのように見せる、といったものです。

CompoundButtonMesh

こちらはボタン状態に応じて、ボタンのScaleや位置、マテリアルのカラーを調整できるスクリプトです。設定はエディタで行うことができます。

たとえば上の設定の場合、ボタンは以下のように動きます。

ボタンにフォーカスを当てたときには表面を薄紅色に変色させ、少し上にポップするようにしています。そしてボタンをタップすると表面の色を赤に変化させ、scaleを縮小させてボタンを潰したかのように見せています。

HoloLensはアプリを操作したときにスマートフォンのような振動等の反応が買ってくるわけではないので、アプリが操作を受け付けていることをユーザに知覚させるために色を変える、大きさを変えるといったデザインが重要になってくるかと思います。

その他の設定項目についても見ていきます。まず一番上の設定項目についてですが、CompoundButtonMeshのボタン設定はシーン上のボタン一つ一つに対して上記の設定していくわけではなく、ScriptableObjectによるボタンのプロファイルを作ってそれを使用します。すなわちボタンの振る舞いをアセットとして用意しておき、そのアセットを割り当てることによって設定を行います。なお、エディタで設定した値はそのプロファイルを上書きするので、注意してください。

MRDesignLabにてデフォルトのプロファイルが用意されていますが、自分で新規に作りたい場合はエディタ上のCreate Profileボタンを押すことによって新しいプロファイルを作成することができます。

作成したプロファイルはCreate Profileボタンのとなりにあるフォームにドラッグ&ドロップすればボタンに適用されます。

続いて、以下2つの項目についてです。

まず、上のTargetObjectですが、これは最初に説明したボタンの状態による移動量(offset)とScaleを適用させるGameObjectを指定しています。例の場合はベースShapeとボタン表面すべてを含むGamePbjectが指定されているため、ボタン全体が動くように見えます。

つづいて下のMeshRendererについてですが、こちらはカラー設定を適用させるマテリアルをもつMeshRendererを指定しています。例の場合ではボタン表面を表すUIButtonSquareFaceを指定しているので、ボタンの表面の色だけが変化します。なお、内部の実装としては、エディタで設定した色情報をマテリアルのシェーダーに渡すことによって色を変化させています。このときエディタのTarget material propertiesのColorの項に指定されているシェーダーパラメータに対して色情報が渡されます。カスタムシェーダーと組み合わせて使う場合は注意してください。

CompoundButtonSounds

ボタン状態やボタンからのイベント発火時に音を鳴らすスクリプトです。設定はCompoundButtonMeshと同じようにプロファイルを作ってボタンに割り当てを行います。(対象の状態とイベントは前章を参照)

Audio Clipをフォームにドラッグ&ドロップして音源を、スライダーでボリュームを設定します。

CompoundButtonAnim

UnityのAnimatorを使ってボタンをアニメーションさせるためのスクリプトです。Animatorの状態遷移を制御する場合、スクリプトを使うことがあると思うのですが、その状態遷移の制御をUnityエディタ上で行うためのスクリプトとイメージすればよいかと思います。

Animatorアセットをエディタ上の「Target Animator」にセットするとエディタ拡張スクリプトがアセットの解析を行い、ボタン状態の一覧とパラメータ選択のリストボックス、パラメータの型に応じたフォームが表示されます。

ここで設定したパラメータと値が、ボタン状態が変化したときにAnimatorにセットされるので、別途制御用のスクリプトを用意することなくアニメーションを設定することができます。

CompoundButtonIcon

ボタンに表示されるアイコンを設定するためのスクリプトです。ここでもプロファイルを使って設定していくのですが、このスクリプトではButtonIconProfileFontとButtonIconProfileTextureの2種類が用意されています。

前者はフォント画像を使用するもので、前回の導入方法にて説明したHoloSymMDL2を使うためのプロファイルです。後者はテクスチャ画像をアイコンとするプロファイルです。

デフォルトでは前者が設定されておりフォントの設定が完了していれば、多くのアイコンを使用することができます。

後者のButtonIconProfileTextureではアイコンの種別名はプリセットが用意されていますが、テクスチャ画像は自分で用意する必要があります。なお、デフォルトで設定されている名前以外のアイコンを作りたい場合は、プロファイルを選択した状態でエディタ下部の「Add custom icon」を押せば、アイコン種別を増やすことができます。

(しかし、新規に作成したプロファイルの場合はエディタ拡張スクリプトにバグがあるので増やすことができません…簡単に直せますが)

ボタンのアイコン設定についてですが、プロファイルを設定すると、それに応じてエディタの表示内容が変わりアイコンを選択できるようになります。

CompoundButtonText

ボタンに表示されるテキストを設定するためのスクリプトです。ButtonHolographicのテキストはTextMeshによって表現されており、このスクリプトはそのTextMeshをコントロールするスクリプトです。こちらもプロファイルを使って設定していくのですが、他のスクリプトと違う点としては、プロファイルよりも優先する設定値を投入できるところです。すなわち、細かい変更ならば、プロファイルをいちいち作らなくても、ボタンごとに個別の設定を行うことができます。

まとめ

MRDesignLabにて提供されているボタンコンポーネントについて、サンプルのButtonHolographicを題材にして基本的な仕組みを解説しました。ボタンの振る舞いを拡張するスクリプトは今回紹介したもの以外にいくつかありますので、興味があったら調べてみるとよいかと思います。

次回は個人的にとても興味深い、BoundingBoxとAppBarについての記事を書きたいと思っています。