Interagierbar — MRTK2

Interactable

Die Interactable Komponente ist ein all-in-one-Container, um ein beliebiges Objekt einfach interagierbar und reaktionsfähig zu machen. Interagierbar fungiert als Catch-All für alle Arten von Eingaben, einschließlich Toucheingabe, Handstrahlen, Spracherkennung usw. und Trichter dieser Interaktionen in Ereignisse und visuelle Designantworten . Diese Komponente bietet eine einfache Möglichkeit, Schaltflächen zu erstellen, Farbe für Objekte mit Fokus zu ändern und vieles mehr.

Konfigurieren von Interagierbaren

Die Komponente ermöglicht drei primäre Konfigurationsabschnitte:

  1. Allgemeine Eingabekonfiguration
  2. Visuelle Designs für mehrere GameObjects
  3. Ereignishandler

Allgemeine Eingabeeinstellungen

General Interactable Settings

Status

Zustände sind ein ScriptableObject-Parameter , der die Interaktionsphase definiert, z. B. drücken oder beobachtet, für interagierbare Profile und visuelle Designs.

Die DefaultInteractableStates (Assets/MRTK/SDK/Features/UX/Interactable/States/DefaultInteractableStates.asset) werden mit MRTK out-of-box ausgeliefert und ist der Standardparameter für interagierbare Komponenten.

States ScriptableObject example in inspector

Die DefaultInteractableStates-Ressource enthält vier Zustände und verwendet die Implementierung des InteractableStates Zustandsmodells.

  • Standard: Nichts geschieht, dies ist der isoliertste Basiszustand.

  • Fokus: Das Objekt wird darauf hingewiesen. Dies ist ein einzelner Zustand, es werden derzeit keine anderen Zustände festgelegt, es wird jedoch der Rang "Standard" angezeigt.

  • Drücken Sie: Das Objekt wird auf eine Taste oder Hand gespitzt. Der Status "Drücken" rangiert "Standard" und "Fokus". Dieser Zustand wird auch als Fallback auf physische Presse festgelegt.

  • Deaktiviert: Die Schaltfläche sollte nicht interaktiv sein und visuelles Feedback wird dem Benutzer mitteilen, ob diese Schaltfläche zu diesem Zeitpunkt nicht verwendet wird. Theoretisch könnte der deaktivierte Zustand alle anderen Zustände enthalten, aber wenn "Aktiviert" deaktiviert ist, trumps der Status "Deaktiviert" alle anderen Zustände.

Je nach Reihenfolge in der Liste wird dem Status ein Bitwert (#) zugewiesen.

Hinweis

Es wird im Allgemeinen empfohlen, die DefaultInteractableStates (Assets/MRTK/SDK/Features/UX/Interactable/States/DefaultInteractableStates.asset) beim Erstellen interagierbarer Komponenten zu verwenden.

Es gibt jedoch 17 Interagierbare Zustände, die verwendet werden können, um Designs zu fördern, obwohl einige von anderen Komponenten gesteuert werden sollen. Hier ist eine Liste der Personen mit integrierter Funktionalität.

  • Besucht: Auf die Interaktion kann geklickt werden.
  • Umschalten: Die Schaltfläche befindet sich in einem Umschaltzustand oder einem Dimension-Index ist eine ungerade Zahl.
  • Geste: Die Hand oder der Controller wurde gedrückt und von der ursprünglichen Position verschoben.
  • VoiceCommand: Ein Sprachbefehl wurde verwendet, um die Interaktionsfunktion auszulösen.
  • PhysicalTouch: Zur Aktivierung wird derzeit eine Toucheingabe erkannt NearInteractionTouchable .
  • Grab: Eine Hand wird derzeit in den Grenzen des Objekts gegriffen, um NearInteractionGrabbable die Aktivierung zu ermöglichen.

Aktiviert

Umschalten, ob eine Interagierbar aktiviert wird oder nicht. Dies entspricht dem Interactable.IsEnabled Code.

Die aktivierte Eigenschaft einer Interagable unterscheidet sich von der aktivierten Eigenschaft, die über GameObject/Component konfiguriert ist (z. B. SetActive usw.). Durch Deaktivieren des GameObject- oder interagierbaren MonoBehaviour wird alles in der Klasse deaktiviert, einschließlich Eingaben, visuellen Designs, Ereignissen usw. Durch deaktivieren sie Interactable.IsEnabled die meisten Eingabebehandlungszustände und setzen verwandte Eingabezustände zurück. Die Klasse führt jedoch weiterhin jeden Frame aus und empfängt Eingabeereignisse, die ignoriert werden. Dies ist nützlich für die Anzeige des Interagierbars in einem deaktivierten Zustand, der über visuelle Designs ausgeführt werden kann. Ein typisches Beispiel hierfür wäre eine Übermittlungsschaltfläche, die auf alle erforderlichen Eingabefelder wartet, die abgeschlossen werden sollen.

Eingabeaktionen

Wählen Sie die Eingabeaktion aus dem Eingabekonfigurations- oder Controllerzuordnungsprofil aus, auf das die interagierbare Komponente reagieren soll.

Diese Eigenschaft kann zur Laufzeit im Code über Interactable.InputAction.

IsGlobal

Wenn true, wird die Komponente als globaler Eingabelistener für die ausgewählte Eingabeaktion markiert. Das Standardverhalten ist falsch, wodurch die Eingabe nur auf diesen interagierbaren Collider/GameObject beschränkt wird.

Diese Eigenschaft kann zur Laufzeit im Code über Interactable.IsGlobal.

Sprachbefehl

Sprachbefehl aus dem MRTK-Sprachbefehlsprofil, um ein OnClick-Ereignis für die Sprachinteraktion auszulösen.

Diese Eigenschaft kann zur Laufzeit im Code über Interactable.VoiceCommand.

Erfordert Fokus

Wenn true, aktiviert der Sprachbefehl nur die Interagierbar , wenn er bereits über einen Zeiger verfügt. Wenn "false", fungiert die Interagierbare als globaler Listener für den ausgewählten Sprachbefehl. Das Standardverhalten ist wahr, da mehrere globale Sprachhörer in einer Szene schwer zu organisieren sein können.

Diese Eigenschaft kann zur Laufzeit im Code über Interactable.VoiceRequiresFocus.

Auswahlmodus

Diese Eigenschaft definiert die Auswahllogik. Wenn auf ein Interagierbares Element geklickt wird, wird er in eine nächste Dimension-Ebene umgewandelt. Dimensionen ähneln rangieren und definieren einen Zustand außerhalb von Eingaben (z. B. Fokus, Drücken usw.). Sie sind nützlich zum Definieren von Umschaltstatus oder anderen mehrstufigen Zuständen, die einer Schaltfläche zugeordnet sind. Die aktuelle Dimension-Ebene wird von Interactable.DimensionIndex.

Die verfügbaren Auswahlmodi sind:

  • Schaltfläche - Dimensionen = 1, einfache klickbare Interaktion
  • Umschalten - Dimensionen = 2, Interagierbare Alternativen zwischen dem Zustand "/Deaktiviert"
  • Mehrdimensionen - Dimensionen> = 3, jeder Klick erhöht die aktuelle Dimensionebene + 1. Nützlich zum Definieren eines Schaltflächenzustands in einer Liste usw.

Interagierbar ermöglicht außerdem, dass mehrere Designs pro Dimension definiert werden können. Wenn z. B. SelectionMode=Umggle verwendet wird, kann ein Design angewendet werden, wenn die Interagierungdeaktiviert ist und ein anderes Design angewendet wird, wenn die Komponente ausgewählt ist.

Der aktuelle Auswahlmodus kann zur Laufzeit abgefragt Interactable.ButtonModewerden. Das Aktualisieren des Modus zur Laufzeit kann erreicht werden, indem Sie die Interactable.Dimensions Eigenschaft auf die gewünschte Funktionalität festlegen. Darüber hinaus kann auf die aktuelle Dimension, die für Umschalt - und Multidimension-Modi nützlich ist, über Interactable.CurrentDimensiondie zugegriffen werden kann.

Interagierbare Profile

Profile sind Elemente, die eine Beziehung zwischen einem GameObject und einem visuellen Design erstellen. Das Profil definiert, welche Inhalte von einem Design bearbeitet werden, wenn eine Zustandsänderung auftritt.

Designs funktionieren viel wie Materialien. Sie sind skriptfähige Objekte, die eine Liste von Eigenschaften enthalten, die einem Objekt basierend auf dem aktuellen Zustand zugewiesen werden. Designs sind auch wieder verwendbar und können über mehrere interagierbare UX-Objekte hinweg zugewiesen werden.

Zurücksetzen auf "Zerstören"

Visuelle Designs ändern verschiedene Eigenschaften für ein gezieltes GameObject, abhängig von der Klasse und dem ausgewählten Designmodultyp. Wenn reset On Destroy true ist, wenn die interagierbare Komponente zerstört wird, setzt die Komponente alle geänderten Eigenschaften von aktiven Designs auf ihre ursprünglichen Werte zurück. Andernfalls bleibt die interagierbare Komponente alle geänderten Eigenschaften wie folgt erhalten. In diesem letzteren Fall wird der letzte Zustand der Werte beibehalten, es sei denn, es wird von einer anderen externen Komponente geändert. Die Standardeinstellung ist „false“.

Profile theams

Ereignisse

Jede interagierbare Komponente verfügt über ein OnClick-Ereignis , das ausgelöst wird, wenn die Komponente einfach ausgewählt ist. Interagierbar kann jedoch verwendet werden, um Andere Eingabeereignisse als nur OnClick zu erkennen.

Klicken Sie auf die Schaltfläche " Ereignis hinzufügen ", um einen neuen Typ der Ereignisempfängerdefinition hinzuzufügen. Wählen Sie nach dem Hinzufügen den Gewünschten Ereignistyp aus.

Events example)

Es gibt verschiedene Arten von Ereignisempfängern, um auf verschiedene Arten von Eingaben zu reagieren. MRTK wird mit den folgenden Empfängern ausgeliefert.

Ein benutzerdefinierter Empfänger kann erstellt werden, indem eine neue Klasse erstellt wird, die erweitert wird ReceiverBase.

Event Toggle Receiver Example

Beispiel für einen Toggle-Ereignisempfänger

Interagierbare Empfänger

Die InteractableReceiver Komponente ermöglicht die Definition von Ereignissen außerhalb der interagierbaren Quellkomponente. Der InteractableReceiver lauscht auf einen gefilterten Ereignistyp, der von einem anderen interagierbaren Ereignistyp ausgelöst wird. Wenn die Interactable-Eigenschaft nicht direkt zugewiesen ist, definiert die Search Scope-Eigenschaft die Richtung, in der die InteractableReceiver-Eigenschaft auf Ereignisse lauscht, die sich entweder selbst, in einem übergeordneten Objekt oder in einem untergeordneten GameObject befinden.

InteractableReceiverList fungiert in ähnlicher Weise, aber für eine Liste der übereinstimmenden Ereignisse.

Interactable reciver

Erstellen benutzerdefinierter Ereignisse

Wie Visual Designs können Ereignisse erweitert werden, um ein beliebiges Zustandsmuster zu erkennen oder Funktionen verfügbar zu machen.

Benutzerdefinierte Ereignisse können auf zwei Hauptmethoden erstellt werden:

  1. Erweitern Sie die ReceiverBase Klasse, um ein benutzerdefiniertes Ereignis zu erstellen, das in der Dropdownliste der Ereignistypen angezeigt wird. Ein Unity-Ereignis wird standardmäßig bereitgestellt, aber zusätzliche Unity-Ereignisse können hinzugefügt werden, oder das Ereignis kann so festgelegt werden, dass Unity-Ereignisse ausgeblendet werden. Mit dieser Funktionalität kann ein Designer mit einem Techniker in einem Projekt arbeiten, um ein benutzerdefiniertes Ereignis zu erstellen, das der Designer im Editor einrichten kann.

  2. Erweitern Sie die ReceiverBaseMonoBehavior Klasse, um eine vollständig benutzerdefinierte Ereigniskomponente zu erstellen, die sich auf dem Interagierbaren oder einem anderen Objekt befinden kann. Der ReceiverBaseMonoBehavior Verweis wird auf die Interagierbare referenziert , um Zustandsänderungen zu erkennen.

Beispiel für die Erweiterung ReceiverBase

Die CustomInteractablesReceiver Klasse zeigt Statusinformationen zu einer Interagierbar an und ist ein Beispiel für das Erstellen eines benutzerdefinierten Ereignisempfängers.

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

Die folgenden Methoden sind hilfreich, um beim Erstellen eines benutzerdefinierten Ereignisempfängers außer Kraft zu setzen/zu implementieren. ReceiverBase.OnUpdate() ist eine abstrakte Methode, die verwendet werden kann, um Zustandsmuster/Übergänge zu erkennen. Darüber hinaus sind die ReceiverBase.OnVoiceCommand() Und ReceiverBase.OnClick() Methoden nützlich, um benutzerdefinierte Ereignislogik zu erstellen, wenn die Interagierbar ausgewählt ist.

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
}
Anzeigen von benutzerdefinierten Ereignisempfängerfeldern im Inspektor

ReceiverBase-Skripts verwenden InspectorField Attribute, um benutzerdefinierte Eigenschaften im Inspektor verfügbar zu machen. Hier ist ein Beispiel für Vector3, eine benutzerdefinierte Eigenschaft mit QuickInfo- und Bezeichnungsinformationen. Diese Eigenschaft wird im Inspektor als konfigurierbar angezeigt, wenn ein interagierbares GameObject ausgewählt ist und der zugeordnete Ereignisempfängertyp hinzugefügt wurde.

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

Verwenden von Interagierbaren

Erstellen einer einfachen Schaltfläche

Eine kann eine einfache Schaltfläche erstellen, indem Sie die interagierbare Komponente zu einem GameObject hinzufügen, das für den Empfang von Eingabeereignissen konfiguriert ist. Es kann einen Kollidieren darauf oder auf einem untergeordneten Element haben, um Eingaben zu empfangen. Wenn Sie interagierbar mit einer Unity UI-basierten GameObjects verwenden, sollte sie sich unter "Canvas GameObject" befinden.

Führen Sie die Schaltfläche einen Schritt weiter, indem Sie ein neues Profil erstellen, das GameObject selbst zuweisen und ein neues Design erstellen. Verwenden Sie außerdem das OnClick-Ereignis , um etwas zu passieren.

Hinweis

Zum Drücken einer Schaltfläche ist die PressableButton Komponente erforderlich. Darüber hinaus ist die PhysicalPressEventRouter Komponente erforderlich, um Ereignisse an die interagierbare Komponente zu trichtern.

Erstellen von Umschaltflächen und Schaltflächen mit mehreren Dimensionen

Umschalter

Wenn Sie eine Schaltfläche umschalten möchten, ändern Sie das Feld so, dass er Selection Mode eingegeben Togglewird. Im Abschnitt "Profile " wird für jedes Profil, das verwendet wird, ein neues Umschaltdesign hinzugefügt, das beim Umschalten der Interaktion verwendet wird.

Während das Kontrollkästchen auf "Umschalten" festgelegt ist, kann das SelectionMode Kontrollkästchen "IsToggled" verwendet werden, um den Standardwert des Steuerelements zur Laufzeitinitialisierung festzulegen.

CanSelect bedeutet, dass die Interagierbaren von unterwegszu gehen können, während die CanDeselect die Umgekehrte bedeutet.

Profile Toggle Visual Themes Example

Entwickler können die SetToggled Und IsToggled Schnittstellen verwenden, um den Umschaltzustand eines interagierbaren Steuerelements über Code abzurufen/festzulegen.

// 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;
Schaltflächensammlung umschalten

Es ist üblich, eine Liste der Umschalttasten zu haben, in denen nur eine jederzeit aktiv sein kann, auch bekannt als radiale Sätze oder Optionsfelder usw.

Verwenden Sie die InteractableToggleCollection Komponente, um diese Funktionalität zu aktivieren. Dieses Steuerelement stellt sicher, dass jeweils nur ein Interagierbares Steuerelement aktiviert ist. Das RadialSet (Assets/MRTK/SDK/Features/UX/Interactable/Prefabs/RadialSet.prefab) ist auch ein großartiger Ausgangspunkt.

So erstellen Sie eine benutzerdefinierte Radialschaltflächengruppe:

  1. Erstellen mehrerer interagierbarer GameObjects/Schaltflächen
  2. Festlegen der einzelnen interagierbaren Elemente mit SelectionMode = Umschaltfläche, CanSelect = true und CanDeselect = false
  3. Erstellen eines leeren übergeordneten GameObject-Elements über alle Interagables und Hinzufügen der InteractableToggleCollection-Komponente
  4. Hinzufügen aller Interagablen zur ToggleList auf der InteractableToggleCollection
  5. Legen Sie die InteractableToggleCollection.CurrentIndex-Eigenschaft fest, um zu bestimmen, welche Schaltfläche standardmäßig beim Start ausgewählt ist.
Toggle collection

Mehrdimensionale Schaltfläche

Der Mehrdimensionsauswahlmodus wird verwendet, um sequenzielle Schaltflächen oder eine Schaltfläche mit mehr als zwei Schritten zu erstellen, z. B. das Steuern der Geschwindigkeit mit drei Werten, Schnelle (1x), Schneller (2x) oder Schnellster (3x).

Bei Dimensionen, die ein numerischer Wert sind, können bis zu 9 Designs hinzugefügt werden, um die Textbeschriftung oder Textur der Schaltfläche für jede Geschwindigkeitseinstellung zu steuern, wobei für jeden Schritt ein anderes Design verwendet wird.

Jedes Klickereignis wird zur DimensionIndex Laufzeit um 1 vorankommen, bis der Dimensions Wert erreicht ist. Anschließend wird der Zyklus auf 0 zurückgesetzt.

Multi-Dimensional profile example

Entwickler können ermitteln DimensionIndex , welche Dimension derzeit aktiv ist.

// 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();

Erstellen von Interagierbar zur Laufzeit

Interagierbar kann jedem GameObject zur Laufzeit ganz einfach hinzugefügt werden. Im folgenden Beispiel wird veranschaulicht, wie Sie einem visuellen Design ein Profil zuweisen.

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()

Interagierbare Ereignisse über Code

Eine Aktion kann dem Basisereignis Interactable.OnClick über Code mit dem folgenden Beispiel hinzugefügt werden.

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

Verwenden Sie die Interactable.AddReceiver<T>() Funktion, um Ereignisempfänger dynamisch zur Laufzeit hinzuzufügen.

Der folgende Beispielcode veranschaulicht, wie Sie einen InteractableOnFocusReceiver hinzufügen, der auf die Eingabe/Beendigung des Fokus lauscht, und definieren darüber hinaus Aktionscode, der ausgeführt werden soll, wenn die Ereignisinstanzen ausgelöst werden.

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"));
}

Der folgende Beispielcode veranschaulicht, wie Sie einen InteractableOnToggleReceiver hinzufügen, der auf ausgewählte/deselected-Zustandsübergänge auf Umschaltfläche-fähige Interagables lauscht und darüber hinaus Aktionscode definiert, der ausgeführt werden soll, wenn die Ereignisinstanzen ausgelöst werden.

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"));
}

Siehe auch