Interactable — MRTK2

対話可能

Interactable コンポーネントは、すべてのオブジェクトを簡単に "対話可能" でレスポンシブ対応にして入力できる、オールインワンのコンテナーです。 Interactable は、タッチ、ハンド レイ、音声など、すべての種類の入力に対応する万能アイテムとして機能し、これらの操作をイベントビジュアル テーマの応答にファネルします。 このコンポーネントを使用すると、ボタンの作成、フォーカスを指定されたオブジェクトの色の変更などを簡単に行うことができます。

Interactable の構成方法

コンポーネントでは、次の 3 つの主な構成セクションを使用できます。

  1. 一般的な入力構成
  2. 複数の GameObject を対象としたビジュアル テーマ
  3. イベント ハンドラー

一般的な入力設定

一般的な対話可能な設定

状態

States (状態) は、Interactable プロファイルビジュアル テーマに対して press (押下) や observed (確認済み) のような操作フェーズを定義する ScriptableObject パラメーターです。

DefaultInteractableStates (Assets/MRTK/SDK/Features/UX/Interactable/States/DefaultInteractableStates.asset) は、MRTK に既定で含まれている、Interactable コンポーネントの既定のパラメーターです。

インスペクターの States ScriptableObject の例

DefaultInteractableStates アセットには、4 つの状態が含まれ、InteractableStates 状態モデルの実装を利用します。

  • Default: 何も発生していません。これは、最も分離された基本状態です。

  • Focus: オブジェクトがポイントされています。 これは 1 つの状態で、他の状態は現在設定されていないものの、Default より優先されます。

  • Press: オブジェクトがポイントされていて、ボタンまたは手が押されています。 Press 状態は Default と Focus より優先されます。 この状態は、Physical Press にもフォールバックとして設定されます。

  • Disabled: ボタンを対話形式にすることはできません。また、この時点で何らかの理由でこのボタンを使用できない場合、視覚的なフィードバックによってユーザーに通知されます。 理論的には、Disabled 状態には他のすべての状態が含まれる可能性がありますが、Enabled がオフの場合、Disabled 状態は他のすべての状態より優先されます。

ビット値 (#) は、一覧内の順序に応じて状態に割り当てられます。

Note

一般に、Interactable コンポーネントの作成時には、DefaultInteractableStates (Assets/MRTK/SDK/Features/UX/Interactable/States/DefaultInteractableStates.asset) を使用することをお勧めします。

ただし、テーマを作動させるのに使用できる Interactable 状態は 17 ですが、他のコンポーネントによって作動される状態もあります。 それらの一覧を組み込みの機能とともに次に示します。

  • Visited: Interactable がクリックされています。
  • Toggled: ボタンが切り替えられた状態であるか、Dimension インデックスが奇数です。
  • Gesture: 手またはコントローラーが押され、元の位置から移動しました。
  • VoiceCommand: Interactable をトリガーするために音声コマンドが使用されました。
  • PhysicalTouch: タッチ入力が検出されています。NearInteractionTouchable を使用して有効にします。
  • Grab: 手でオブジェクトの境界をつかんでいます。NearInteractionGrabbable を使用して有効にします。

有効

Interactable を有効にして開始するかどうかを切り替えます。 これは、コード内の Interactable.IsEnabled に対応します。

Interactable の Enabled プロパティは、GameObject/Component (SetActive など) を使用して構成された Enabled プロパティとは異なります。 GameObject または Interactable MonoBehaviour を無効にすると、入力、ビジュアル テーマ、イベントなど、クラス内のすべての実行が無効になります。Interactable.IsEnabled を使用して無効にすると、ほとんどの入力処理が無効になり、関連する入力状態がリセットされます。 ただし、クラスでは引き続きすべてのフレームが実行され、無視される入力イベントが受信されます。 これは、ビジュアル テーマを使用して実行できる無効な状態で Interactable を表示する場合に便利です。 この一般的な例は、必要なすべての入力フィールドが入力されるまで待機する送信ボタンです。

入力アクション

Interactable コンポーネントが応答する必要のある入力構成またはコントローラー マッピング プロファイルから入力アクションを選択します。

このプロパティは、実行時に Interactable.InputAction を使用してコードで構成できます。

IsGlobal

true の場合、選択した入力アクションのグローバル入力リスナーとしてコンポーネントがマークされます。 既定の動作は false で、入力はこの Interactable コライダーまたは GameObject のみに制限されます。

このプロパティは、実行時に Interactable.IsGlobal を使用してコードで構成できます。

Speech コマンド

音声で操作するために OnClick イベントをトリガーする、MRTK Speech Commands Profile の Speech コマンド

このプロパティは、実行時に Interactable.VoiceCommand を使用してコードで構成できます。

Requires Focus (フォーカスが必要)

true の場合、音声コマンドは、ポインターから既にフォーカスが指定されている場合にのみ、Interactable をアクティブにします。 false の場合、Interactable は選択された音声コマンドのグローバル リスナーとして機能します。 既定の動作は true です。これは、ある場面では、複数のグローバル音声リスナーを整理するのが難しい場合があるためです。

このプロパティは、実行時に Interactable.VoiceRequiresFocus を使用してコードで構成できます。

Selection Mode (選択モード)

このプロパティは、選択ロジックを定義します。 Interactable をクリックすると、次の Dimension レベルに反復処理されます。 Dimensions はランクに似ており、入力以外の状態 (フォーカスや押下など) を定義します。 これらは、Toggle 状態またはボタンに関連付けられているその他の複数ランクの状態を定義する場合に便利です。 現在の Dimension レベルは、Interactable.DimensionIndex によって追跡されます。

使用可能な選択モードは次のとおりです。

  • Button - Dimensions = 1。シンプルかつクリック可能な Interactable です
  • Toggle - Dimensions = 2。Interactable により "オン"/"オフ" の状態が切り替えられます
  • Multi-Dimension - Dimensions> = 3。クリックするたびに現在の Dimension レベルが 1 ずつ増加します。 ボタンの状態を一覧などに定義する場合に便利です

Interactable では、Dimension ごとに複数のテーマを定義することもできます。 たとえば、SelectionMode = Toggle の場合、Interactable を "選択解除" したときにあるテーマが適用され、コンポーネントを "選択" したときには別のテーマが適用されることがあります。

現在の Selection Mode は、実行時に Interactable.ButtonMode を使用して照会できます。 実行時にモードを更新するには、目的の機能に合わせて Interactable.Dimensions プロパティを設定します。 さらに、Toggle (トグル) および Multi-Dimension (多次元) モードに役立つ現在のディメンションは、Interactable.CurrentDimension を使用してアクセスできます。

Interactable プロファイル

Profiles は、GameObject とビジュアル テーマの間に関係を作成するアイテムです。 プロファイルにより、状態の変更が発生したときにテーマによって操作されるコンテンツが定義されます。

テーマは素材とほぼ同様に機能します。 これらはスクリプト可能なオブジェクトであり、現在の状態に基づいてオブジェクトに割り当てられるプロパティの一覧が含まれます。 また、テーマは再利用可能であり、複数の Interactable UX オブジェクトに割り当てられます。

Reset On Destroy (破棄時にリセット)

ビジュアル テーマは、選択したテーマ エンジンのクラスと種類に応じて、対象の GameObject のさまざまなプロパティを変更します。 Interactable コンポーネントが破棄されたときに [破棄時にリセット] が true の場合、コンポーネントは、アクティブなテーマの変更されたすべてのプロパティを、元の値にリセットします。 それ以外の場合、破棄されると、Interactable コンポーネントは変更されたプロパティをそのまま残します。 この後者の場合、別の外部コンポーネントによって変更されない限り、値の最終状態が保持されます。 既定値は false です。

プロファイルの概要

イベント

すべての Interactable コンポーネントには、コンポーネントが選択されただけで発生する OnClick イベントがあります。 ただし、Interactable を使用して、OnClick 以外の入力イベントを検出することができます。

[イベントの追加] ボタンをクリックして、新しい種類のイベント レシーバーの定義を追加します。 追加したら、必要なイベントの種類を選択します。

イベントの例)

さまざまな種類の入力に応答する、さまざまな種類のイベント レシーバーがあります。 MRTK には、既定で次の一連のレシーバーが含まれます。

カスタム レシーバーを作成するには、ReceiverBase を拡張する新しいクラスを作成します。

イベント トグル レシーバーの例

Toggle イベントのレシーバーの例

Interactable レシーバー

InteractableReceiver コンポーネントでは、ソースの Interactable コンポーネントの外部でイベントを定義できます。 InteractableReceiver は、別の Interactable によって起動され、フィルター処理されたイベントの種類をリッスンします。 Interactable プロパティが直接割り当てられていない場合は、Search Scope プロパティによって、InteractableReceiver が自身、親、または子 GameObject にあるイベントをリッスンする指示が定義されます。

InteractableReceiverList は同様の方法で動作しますが、一致するイベントの一覧に対して機能します。

対話可能な reciver

カスタム イベントを作成する

ビジュアル テーマと同様に、イベントを拡張して、任意の状態パターンを検出したり、機能を公開したりすることができます。

カスタム イベントは、主に次の 2 つの方法で作成できます。

  1. ReceiverBase クラスを拡張して、カスタム イベントを作成します。これは、イベントの種類のドロップダウン リストに表示されます。 Unity イベントは既定で入力されていますが、Unity イベントを追加するか、Unity イベントを非表示にするようにイベントを設定できます。 この機能により、デザイナーはプロジェクトでエンジニアと共同作業して、カスタム イベントを作成できます。これは、デザイナーがエディターで設定できます。

  2. ReceiverBaseMonoBehavior クラスを拡張して、Interactable または別のオブジェクトに格納できる、完全にカスタムなイベント コンポーネントを作成します。 ReceiverBaseMonoBehavior は、状態の変化を検出するために Interactable を参照します。

ReceiverBase の拡張の例

CustomInteractablesReceiver クラスは、Interactable に関する状態の情報を表示します。これは、カスタム イベント レシーバーを作成する方法の例です。

public CustomInteractablesReceiver(UnityEvent ev) : base(ev, "CustomEvent")
{
    HideUnityEvents = true; // hides Unity events in the receiver - meant to be code only
}

次のメソッドは、カスタム イベント レシーバーの作成時に上書きまたは実装する場合に役立ちます。 ReceiverBase.OnUpdate() は、状態パターンおよび遷移を検出するのに使用できる抽象メソッドです。 また、ReceiverBase.OnVoiceCommand() および ReceiverBase.OnClick() メソッドは、Interactable を選択したときにカスタム イベント ロジックを作成する場合に役立ちます。

public override void OnUpdate(InteractableStates state, Interactable source)
{
    if (state.CurrentState() != lastState)
    {
        // the state has changed, do something new
        lastState = state.CurrentState();
        ...
    }
}

public virtual void OnVoiceCommand(InteractableStates state, Interactable source,
                                    string command, int index = 0, int length = 1)
{
    base.OnVoiceCommand(state, source, command, index, length);
    // voice command called, perform some action
}  

public virtual void OnClick(InteractableStates state,
                            Interactable source,
                            IMixedRealityPointer pointer = null)
{
    base.OnClick(state, source);
    // click called, perform some action
}
インスペクターでのカスタム イベント レシーバー フィールドの表示

ReceiverBase スクリプトでは、InspectorField 属性を使用して、インスペクターでカスタム プロパティを公開します。 カスタム プロパティの Vector3 の例を、ヒントとラベル情報とともに紹介します。 このプロパティは、Interactable GameObject が選択されていて、関連付けられた "イベント レシーバー" の種類が追加されている場合に、インスペクターで構成可能として表示されます。

[InspectorField(Label = "<Property label>",Tooltip = "<Insert tooltip info>",Type = InspectorField.FieldTypes.Vector3)]
public Vector3 EffectOffset = Vector3.zero;

Interactable の使用方法

シンプルなボタンの作成

入力イベントを受信するように構成された GameObject に Interactable コンポーネントを追加することで、シンプルなボタンを作成できます。 入力を受信する GameObject または子にコライダーを設定できます。 Unity UI ベースの GameObject で Interactable を使用する場合、Canvas GameObject の下に配置する必要があります。

新しいプロファイルを作成し、GameObject 自体を割り当て、新しいテーマを作成して、ボタンを一歩先に進めます。 さらに、OnClick イベントを使用して何かを起こすことができます。

Note

ボタンを押下可能にするには、PressableButton コンポーネントが必要です。 さらに、Interactable コンポーネントに press (押下) イベントをファネルするために、PhysicalPressEventRouter コンポーネントが必要です。

トグルおよび多次元ボタンの作成

トグル ボタン

ボタンにトグルを設定するには、Selection Mode フィールドの種類を Toggle に変更します。 [プロファイル] セクションでは、Interactable をオンに切り替えたときに使用される各プロファイルに対して、新しい切り替えテーマが追加されます。

SelectionMode が [トグル] に設定されると、IsToggled チェックボックスを使用して、実行時に初期化されたときのコントロールの既定値を設定できます。

[CanSelect] は、Interactable が "オフ" から "オン" に移行できることを意味し、[CanDeselect] はその逆を意味します。

プロファイルの切り替えのビジュアル テーマの例

開発者は、SetToggled インターフェイスと IsToggled インターフェイスを利用して、コードにより Interactable のトグル状態を取得および設定できます。

// If using SelectionMode = Toggle (i.e Dimensions == 2)

// Make the Interactable selected and toggled on
myInteractable.IsToggled = true;

// Get whether the Interactable is selected or not
bool isSelected = myInteractable.IsToggled;
トグル ボタンのコレクション

通常、任意の時点で 1 つのみをアクティブにできるトグル ボタン (放射状セットやラジオ ボタンとも呼ばれます) の一覧があります。

この機能を有効にするには、InteractableToggleCollection コンポーネントを使用します。 このコントロールにより、任意の時点で 1 つの Interactable のみがオンに切り替えられます。 また、既定で備わっている RadialSet (Assets/MRTK/SDK/Features/UX/Interactable/Prefabs/RadialSet.prefab) も、適切な開始点です。

カスタム放射状ボタン グループの作成方法:

  1. 複数の Interactable GameObjects/ボタンを作成する
  2. Interactableで、SelectionMode = Toggle、CanSelect = true、CanDeselect = false にそれぞれ設定する
  3. すべての Interactables で空白の親 GameObject を作成して、InteractableToggleCollection コンポーネントを追加する
  4. InteractableToggleCollectionToggleList にすべての Interactables を追加する
  5. InteractableToggleCollection.CurrentIndex プロパティを設定して、起動時に既定で選択されるボタンを決定する
コレクションの切り替え

多次元ボタン

Multi-Dimension (多次元) 選択モードは、シーケンシャルなボタン、または 3 つ以上のステップを持つボタンを作成するのに使用されます。たとえば、速い (1 倍)、高速 (2 倍)、最速 (3 倍) の 3 つの値を使用した速度調整などです。

次元が数値の場合、最大 9 つのテーマを追加でき、ステップごとに異なるテーマを使用して、それぞれの速度設定のボタンのテキスト ラベルまたはテクスチャを調整できます。

各クリック イベントでは、Dimensions の値に達するまで、実行時に DimensionIndex から 1 ずつ進みます。 その後、サイクルは 0 にリセットされます。

多次元プロファイルの例

開発者は DimensionIndex を評価して、現在アクティブな次元を特定できます。

// If using SelectionMode = Multi-dimension (i.e Dimensions >= 3)

//Access the current DimensionIndex
int currentDimension = myInteractable.CurrentDimension;

//Set the current DimensionIndex to 2
myInteractable.CurrentDimension = 2;

// Promote Dimension to next level
myInteractable.IncreaseDimension();

実行時に Interactable を作成する

Interactable は、実行時に任意の GameObject に簡単に追加できます。 次の例は、ビジュアル テーマとともにプロファイルを割り当てる方法を示しています。

var interactableObject = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
var interactable = interactableObject.AddComponent<Interactable>();

// Get the default configuration for the Theme engine InteractableColorTheme
var newThemeType = ThemeDefinition.GetDefaultThemeDefinition<InteractableColorTheme>().Value;

// Define a color for every state in our Default Interactable States
newThemeType.StateProperties[0].Values = new List<ThemePropertyValue>()
{
    new ThemePropertyValue() { Color = Color.black},  // Default
    new ThemePropertyValue() { Color = Color.black}, // Focus
    new ThemePropertyValue() { Color = Random.ColorHSV()},   // Pressed
    new ThemePropertyValue() { Color = Color.black},   // Disabled
};

interactable.Profiles = new List<InteractableProfileItem>()
{
    new InteractableProfileItem()
    {
        Themes = new List<Theme>()
        {
            Interactable.GetDefaultThemeAsset(new List<ThemeDefinition>() { newThemeType })
        },
        Target = interactableObject,
    },
};

// Force the Interactable to be clicked
interactable.TriggerOnClick()

コードを使用した Interactable イベント

次の例を参照して、コードを使用して基本の Interactable.OnClick イベントにアクションを追加できます。

public static void AddOnClick(Interactable interactable)
{
    interactable.OnClick.AddListener(() => Debug.Log("Interactable clicked"));
}

Interactable.AddReceiver<T>() 関数を使用して、実行時にイベント レシーバーを動的に追加します。

下のコード例は、InteractableOnFocusReceiver を追加する方法を示しています。これは、フォーカスの入力/終了をリッスンし、さらにイベント インスタンスの発生時に実行するアクション コードを定義します。

public static void AddFocusEvents(Interactable interactable)
{
    var onFocusReceiver = interactable.AddReceiver<InteractableOnFocusReceiver>();

    onFocusReceiver.OnFocusOn.AddListener(() => Debug.Log("Focus on"));
    onFocusReceiver.OnFocusOff.AddListener(() => Debug.Log("Focus off"));
}

下のコード例は、InteractableOnToggleReceiver を追加する方法を示しています。これは、トグルを設定した Interactables で選択/選択解除された状態の遷移をリッスンし、さらにイベント インスタンスの発生時に実行するアクション コードを定義します。

public static void AddToggleEvents(Interactable interactable)
{
    var toggleReceiver = interactable.AddReceiver<InteractableOnToggleReceiver>();

    // Make the interactable have toggle capability, from code.
    // In the gui editor it's much easier
    interactable.Dimensions = 2;
    interactable.CanSelect = true;
    interactable.CanDeselect  = true;

    toggleReceiver.OnSelect.AddListener(() => Debug.Log("Toggle selected"));
    toggleReceiver.OnDeselect.AddListener(() => Debug.Log("Toggle un-selected"));
}

関連項目