Visuelle Designs — MRTK2

Designs ermöglichen die flexible Steuerung von UX-Ressourcen als Reaktion auf verschiedene Statusübergänge. Dies kann das Ändern der Farbe einer Schaltfläche, das Ändern der Größe eines Elements als Reaktion auf den Fokus usw. umfassen. Das Visual Designs-Framework besteht aus zwei Schlüsselteilen: 1) Konfiguration und 2) Laufzeitmodulen.

Designkonfigurationen sind Definitionen von Eigenschaften und Typen, während Designmodule Klassen sind, die die Konfigurationen nutzen und die Logik implementieren, um Transformationen, Materialien und mehr zur Laufzeit zu aktualisieren.

Designkonfiguration

Designkonfigurationen sind ScriptableObjects, die definieren, wie Theme Engines zur Laufzeit initialisiert werden. Sie definieren, welche Eigenschaften und Werte als Reaktion auf Eingabe- oder andere Statusänderungen verwendet werden sollen, wenn die App ausgeführt wird. Als ScriptableObjects-Ressourcen können Designkonfigurationen einmal definiert und dann in verschiedenen UX-Komponenten erneut verwendet werden.

So erstellen Sie ein neues Theme Objekt:

  1. Klicken Sie mit der rechten Maustaste im fenster Project
  2. Auswählen des Designs "ErstellenMixed Reality Toolkit>">

Beispieldesignkonfigurationsressourcen finden Sie unter MRTK/SDK/Features/UX/Interactable/Themes.

Theme ScriptableObject example in inspector

Zustände

Beim Erstellen eines neuen Theme, das erste, was festgelegt werden soll, ist, welche Zustände verfügbar sind. Die States-Eigenschaft gibt an, wie viele Werte eine Designkonfiguration definieren muss, da pro Zustand ein Wert vorhanden ist. Im obigen Beispielbild sind die für die interagierbaren Komponenten definierten Standardzustände,Fokus, Gedrückt unddeaktiviert. Diese werden in der DefaultInteractableStates Objektdatei (Assets/MRTK/SDK/Features/UX/Interactable/States) definiert.

So erstellen Sie ein neues State Objekt:

  1. Klicken Sie mit der rechten Maustaste im fenster Project
  2. Wählen Sie "ErstellenMixed Reality Toolkitstatus>"> aus.

States ScriptableObject example in inspector

Ein State ScriptableObject definiert sowohl die Liste der Zustände als auch den Typ von StateModel , der für diese Zustände erstellt werden soll. Ein StateModel ist eine Klasse, die die Zustandscomputerlogik erweitert BaseStateModel und implementiert, um den aktuellen Zustand zur Laufzeit zu generieren. Der aktuelle Zustand dieser Klasse wird in der Regel von Theme Engines zur Laufzeit verwendet, um zu diktieren, welche Werte für Materialeigenschaften, GameObject-Transformationen und vieles mehr festgelegt werden sollen.

Designmoduleigenschaften

Außerhalb von Staaten definiert ein Theme Objekt auch eine Liste von Designmodulen und die zugehörigen Eigenschaften für diese Engines. Ein Designmodul definiert erneut die Logik, um die richtigen Werte für ein GameObject zur Laufzeit festzulegen.

Ein Theme Objekt kann mehrere Designmodule definieren, um anspruchsvolle visuelle Zustände für mehrere GameObject-Eigenschaften zu erzielen.

Designlaufzeit

Definiert den Klassentyp des Designmoduls, das erstellt wird

Lockerung

Wenn sie ihre Eigenschaft IsEasingSupported als "true" definieren, unterstützen einige Designmodule die Beschleunigung zwischen Zuständen. Beispiel: Lerping zwischen zwei Farben, wenn eine Zustandsänderung auftritt. Die Dauer definiert in Sekunden, wie lange der Startwert bis zum Endwert erleichtert werden kann, und die Animationskurve definiert die Änderungsrate während dieses Zeitraums.

Shadereigenschaften

Einige Designmodule, wenn sie ihre Eigenschaft AreShadersSupported als true definieren, ändern bestimmte Shadereigenschaften zur Laufzeit. Die Felder "Shader " und "Eigenschaft " definieren die Shadereigenschaft für das Ziel.

Erstellen einer Designkonfiguration über Code

Im Allgemeinen ist es einfacher, Designkonfigurationen über den Unity Inspector zu entwerfen, aber es gibt Fälle, in denen Designs zur Laufzeit dynamisch über Code generiert werden müssen. Der folgende Codeausschnitt enthält ein Beispiel für die Ausführung dieser Aufgabe.

Um die Entwicklung zu beschleunigen, sind die folgenden Hilfsmethoden hilfreich, um die Einrichtung zu vereinfachen.

Interactable.GetDefaultInteractableStates() – erstellt ein neues States ScriptableObject mit den vier Standardwerten, die in der interagierbaren Komponente verwendet werden.

ThemeDefinition.GetDefaultThemeDefinition<T>() - Jedes Designmodul definiert eine Standardkonfiguration mit den richtigen Eigenschaften, die für diesen Designlaufzeittyp erforderlich sind. Mit diesem Hilfsprogramm wird eine Definition für den angegebenen Designmodultyp erstellt.

// This code example builds a Theme ScriptableObject that can be used with an Interactable component.
// A random color is selected for the on pressed state every time this code is executed.

// Use the default states utilized in the Interactable component
var defaultStates = Interactable.GetDefaultInteractableStates();

// 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
};

// Create the Theme configuration asset
Theme testTheme = ScriptableObject.CreateInstance<Theme>();
testTheme.States = defaultStates;
testTheme.Definitions = new List<ThemeDefinition>() { newThemeType };

Designmodule

Ein Designmodul ist eine Klasse, die sich aus der InteractableThemeBase Klasse erstreckt. Diese Klassen werden zur Laufzeit instanziiert und mit einem ThemeDefinition Objekt wie zuvor beschrieben konfiguriert.

Standarddesignmodulen

MRTK wird mit einem Standardsatz von Designmodulen ausgeliefert, die unten aufgeführt sind:

Die Standarddesignmodule finden Sie unter MRTK/SDK/Features/UX/Scripts/VisualThemes/ThemeEngines.

Benutzerdefinierte Designmodule

Wie angegeben, wird ein Designmodul als Klasse definiert, die sich aus der InteractableThemeBase Klasse erstreckt. Daher muss das neue Designmodul diese Klasse nur erweitern und folgendes implementieren:

Obligatorische Implementierungen

public abstract void SetValue(ThemeStateProperty property, int index, float percentage) (xref:Microsoft.MixedReality.Toolkit.UI.InteractableThemeBase.SetValue)

Legen Sie für die angegebene Eigenschaft den aktuellen Statuswert für den gezielten GameObject-Host fest ThemeStateProperty.Name(d. h. die Materialfarbe usw.). Der Index gibt den aktuellen Zustandswert für den Zugriff und den Prozentsatz an, ein Float zwischen 0 und 1, wird für beschleunigungs-/lerping zwischen Werten verwendet.

public abstract ThemePropertyValue GetProperty(ThemeStateProperty property)(xref:Microsoft.MixedReality.Toolkit.UI.InteractableThemeBase.GetProperty)

Geben Sie für die angegebene Eigenschaft, durch die identifiziert werden kann, den aktuellen Wert zurück, der für das zielorientierte Host GameObject festgelegt ist ThemeStateProperty.Name(z. B. die aktuelle Materialfarbe, der aktuelle lokale Positionsversatz usw.). Dies wird hauptsächlich zum Zwischenspeichern des Startwerts verwendet, wenn die Beschleunigung zwischen Zuständen erfolgt.

public abstract ThemeDefinition GetDefaultThemeDefinition()(xref:Microsoft.MixedReality.Toolkit.UI.InteractableThemeBase.GetDefaultThemeDefinition)

Gibt ein ThemeDefinition Objekt zurück, das die standardeigenschaften und -konfiguration definiert, die für das benutzerdefinierte Design erforderlich sind.

protected abstract void SetValue(ThemeStateProperty property, ThemePropertyValue value)

Geschützte Variante der öffentlichen SetValue() Definition, mit Ausnahme von ThemePropertyValue, die festgelegt werden soll, anstatt die Index- und/oder Prozentkonfiguration zu verwenden.

InteractableThemeBase.Init(GameObject host, ThemeDefinition settings)

Führen Sie hier alle Initialisierungsschritte aus, die auf den bereitgestellten GameObject-Parameter ausgerichtet sind, und verwenden Sie die eigenschaften und Konfigurationen, die im ThemeDefinition-Parameter definiert sind. Es wird empfohlen, am Anfang einer Außerkraftsetzung aufzurufen base.Init(host, settings) .

InteractableThemeBase.IsEasingSupported

Wenn das benutzerdefinierte Designmodul die Beschleunigung zwischen Werten unterstützen kann, die über die ThemeDefinition.Easing Eigenschaft konfiguriert sind.

InteractableThemeBase.AreShadersSupported

Wenn das benutzerdefinierte Designmodul shadereigenschaften unterstützen kann. Es wird empfohlen, von InteractableShaderTheme der vorhandenen Infrastruktur zu profitieren, um Shadereigenschaften über MaterialPropertyBlocks effizient festzulegen/abzurufen. Die Shader-Eigenschaftsinformationen werden in jedem ThemeStateProperty über ThemeStateProperty.TargetShader und .ThemeStateProperty.ShaderPropertyName

Hinweis

Wenn die Erweiterung erweitert InteractableShaderThemewird, kann es auch nützlich sein, das InteractableShaderTheme.DefaultShaderProperty über new zu überschreiben.

Beispielcode: protected new const string DefaultShaderProperty = "_Color";

Darüber hinaus erweitern die folgenden Klassen die Klasse, die InteractableShaderTheme erneut MaterialPropertyBlocks verwendet, um Shadereigenschaftenwerte zu ändern. Dieser Ansatz hilft der Leistung , da MaterialPropertyBlocks beim Ändern von Werten keine neuen instanzierten Materialien erstellen. Der Zugriff auf die typischen Materialklasseneigenschaften gibt jedoch keine erwarteten Werte zurück. Verwenden Sie MaterialPropertyBlocks , um aktuelle Materialeigenschaftenwerte abzurufen und zu überprüfen (z. B. _Color oder _MainTex).

InteractableThemeBase.Reset

Leitet das Design um, alle geänderten Eigenschaften wieder auf ihre ursprünglichen Werte zurückzusetzen, die beim Initialisieren dieses Designmoduls auf dem Host GameObject festgelegt wurden.

Beispiel für ein benutzerdefiniertes Designmodul

Die folgende Klasse ist ein Beispiel für ein benutzerdefiniertes neues Designmodul. Diese Implementierung findet eine MeshRenderer-Komponente für das initialisierte Hostobjekt und steuert die Sichtbarkeit basierend auf dem aktuellen Zustand.

using Microsoft.MixedReality.Toolkit.UI;
using System;
using System.Collections.Generic;
using UnityEngine;

// This class demonstrates a custom theme to control a Host's MeshRenderer visibility
public class MeshVisibilityTheme : InteractableThemeBase
{
    // Bool visibility does not make sense for lerping
    public override bool IsEasingSupported => false;

    // No material or shaders are being modified
    public override bool AreShadersSupported => false;

    // Cache reference to the MeshRenderer component on our Host
    private MeshRenderer meshRenderer;

    public MeshVisibilityTheme()
    {
        Types = new Type[] { typeof(MeshRenderer) };
        Name = "Mesh Visibility Theme";
    }

    // Define a default configuration to simplify initialization of this theme engine
    // There is only one state property with a value per available state
    // This state property is a boolean that defines whether the renderer is enabled
    public override ThemeDefinition GetDefaultThemeDefinition()
    {
        return new ThemeDefinition()
        {
            ThemeType = GetType(),
            StateProperties = new List<ThemeStateProperty>()
            {
                new ThemeStateProperty()
                {
                    Name = "Mesh Visible",
                    Type = ThemePropertyTypes.Bool,
                    Values = new List<ThemePropertyValue>(),
                    Default = new ThemePropertyValue() { Bool = true }
                },
            },
            CustomProperties = new List<ThemeProperty>()
        };
    }

    // When initializing, cache a reference to the MeshRenderer component
    public override void Init(GameObject host, ThemeDefinition definition)
    {
        base.Init(host, definition);

        meshRenderer = host.GetComponent<MeshRenderer>();
    }

    // Get the current state of the MeshRenderer visibility
    public override ThemePropertyValue GetProperty(ThemeStateProperty property)
    {
        return new ThemePropertyValue()
        {
            Bool = meshRenderer.enabled
        };
    }

    // Update the MeshRenderer visibility based on the property state value data
    public override void SetValue(ThemeStateProperty property, int index, float percentage)
    {
        meshRenderer.enabled = property.Values[index].Bool;
    }
}

Vollständiges Beispiel

Das folgende Codebeispiel veranschaulicht, wie dieses Design zur Laufzeit gesteuert wird. Insbesondere, wie sie den aktuellen Zustand für das Design festlegen, sodass die MeshRenderer-Sichtbarkeit entsprechend aktualisiert wird.

Hinweis

theme.OnUpdate(state,force) sollte in der Regel in der Update()-Methode aufgerufen werden, um Theme Engines zu unterstützen, die beschleunigungs-/lerping zwischen Werten verwenden.

using Microsoft.MixedReality.Toolkit.UI;
using System;
using System.Collections.Generic;
using UnityEngine;

public class MeshVisibilityController : MonoBehaviour
{
    private MeshVisibilityTheme themeEngine;
    private bool hideMesh = false;

    private void Start()
    {
        // Define the default configuration. State 0 will be on while State 1 will be off
        var themeDefinition = ThemeDefinition.GetDefaultThemeDefinition<MeshVisibilityTheme>().Value;
        themeDefinition.StateProperties[0].Values = new List<ThemePropertyValue>()
        {
            new ThemePropertyValue() { Bool = true }, // show state
            new ThemePropertyValue() { Bool = false }, // hide state
        };

        // Create the actual Theme engine and initialize it with the GameObject we are attached to
        themeEngine = (MeshVisibilityTheme)InteractableThemeBase.CreateAndInitTheme(themeDefinition, this.gameObject);
    }

    private void Update()
    {
        // Update the theme engine to set our MeshRenderer visibility
        // based on our current state (i.e the hideMesh variable)
        themeEngine.OnUpdate(Convert.ToInt32(hideMesh));
    }

    public void ToggleVisibility()
    {
        // Alternate state of visibility
        hideMesh = !hideMesh;
    }
}

Siehe auch