Visuelle Designs — MRTK2

Designs ermöglichen eine 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 Themes-Framework besteht aus zwei Schlüsseln: 1) Konfiguration und 2) Runtime-Engines.

Designkonfigurationen sind Definitionen von Eigenschaften und Typen, während Design-Engines 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 Design-Engines zur Laufzeit initialisiert werden. Sie definieren, welche Eigenschaften und Werte als Reaktion auf Eingabe- oder andere Zustandsänderungen verwendet werden sollen, wenn die App ausgeführt wird. Als ScriptableObjects-Ressourcen können Designkonfigurationen einmal definiert und dann für verschiedene UX-Komponenten wiederverwendet werden.

So erstellen Sie ein neues Theme Asset:

  1. Klicken Sie mit der rechten Maustaste im Projektfenster
  2. Wählen Sie Erstellen>Mixed Reality Toolkit-Design>aus.

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

DesignskriptableObject-Beispiel im Inspektor

Zustände

Beim Erstellen eines neuen Thememüssen Sie zunächst festlegen, welche Zustände verfügbar sind. Die States-Eigenschaft gibt an, wie viele Werte eine Designkonfiguration definieren muss, da es einen Wert pro Zustand gibt. Im obigen Beispielbild sind die Standardzustände, die für die Interaktive Komponente definiert sind , Standard, Fokus, Pressed und Disabled. Diese werden in der DefaultInteractableStates Assetdatei (Assets/MRTK/SDK/Features/UX/Interactable/States) definiert.

So erstellen Sie ein neues State Asset:

  1. Klicken Sie mit der rechten Maustaste im Projektfenster
  2. Wählen Sie Erstellen>Mixed Reality Toolkitstatus> aus.

SkriptableObject-Beispiel für Zustände im Inspektor

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 Design-Engines zur Laufzeit verwendet, um festzulegen, welche Werte für Materialeigenschaften, GameObject-Transformationen und mehr festgelegt werden sollen.

Eigenschaften der Design-Engine

Außerhalb von Staaten definiert ein Theme Asset auch eine Liste von Design-Engines und den zugehörigen Eigenschaften für diese Engines. Eine Design-Engine definiert erneut die Logik, um die richtigen Werte für ein GameObject zur Laufzeit festzulegen.

Ein Theme Asset kann mehrere Design-Engines definieren, um anspruchsvolle visuelle Statusübergänge für mehrere GameObject-Eigenschaften zu erzielen.

Theme Runtime

Definiert den Klassentyp der Design-Engine, die erstellt wird.

Lockerung

Einige Design-Engines unterstützen, wenn sie ihre Eigenschaft IsEasingSupported als true definieren, die Erleichterung zwischen Denkzuständen. Beispiel: Lerping zwischen zwei Farben, wenn eine Zustandsänderung auftritt. Die Dauer definiert in Sekunden, wie lange vom Startwert zum Endwert erleichtert werden soll, und die Animationskurve definiert die Änderungsrate während dieses Zeitraums.

Shadereigenschaften

Wenn einige Design-Engines ihre Eigenschaft AreShadersSupported als true definieren, ändern sie bestimmte Shadereigenschaften zur Laufzeit. Die Felder Shader und Eigenschaft definieren die Shadereigenschaft, die als Ziel verwendet werden soll.

Erstellen einer Designkonfiguration über Code

Im Allgemeinen ist es einfacher, Designkonfigurationen über den Unity-Inspektor 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 nützlich, um das Setup zu vereinfachen.

Interactable.GetDefaultInteractableStates() – Erstellt ein neues ZustandsskriptableObject mit den vier Standardwerten, die in der Interaktionsfähigen Komponente verwendet werden.

ThemeDefinition.GetDefaultThemeDefinition<T>() – Jede Design-Engine definiert eine Standardkonfiguration mit den richtigen Eigenschaften, die für diesen Designlaufzeittyp erforderlich sind. Dieses Hilfsprogramm erstellt eine Definition für den angegebenen Design-Engine-Typ.

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

Design-Engines

Eine Design-Engine ist eine Klasse, die von der InteractableThemeBase -Klasse aus erweitert wird. Diese Klassen werden zur Laufzeit instanziiert und mit einem ThemeDefinition -Objekt konfiguriert, wie zuvor beschrieben.

Standarddesign-Engines

MRTK wird mit einem Standardsatz von Design-Engines ausgeliefert, die unten aufgeführt sind:

Die Standarddesign-Engines finden Sie unter MRTK/SDK/Features/UX/Scripts/VisualThemes/ThemeEngines.

Benutzerdefinierte Design-Engines

Wie bereits erwähnt, wird eine Design-Engine als Klasse definiert, die sich von der InteractableThemeBase -Klasse aus erweitert. Daher muss die neue Design-Engine nur diese Klasse erweitern und Folgendes implementieren:

Obligatorische Implementierungen

public abstract void SetValue(ThemeStateProperty property, int index, float percentage)

Legen Sie für die angegebene Eigenschaft, die durch ThemeStateProperty.Nameidentifiziert werden kann, ihren aktuellen Zustandswert auf dem zielorientierten GameObject-Host fest (d. h. die Materialfarbe usw.). Der Index gibt den aktuellen Zustandswert an, auf den zugegriffen werden soll, und der Prozentsatz, ein Float zwischen 0 und 1, wird für die Beschleunigung/Lerping zwischen Werten verwendet.

public abstract ThemePropertyValue GetProperty(ThemeStateProperty property)

Geben Sie für die angegebene Eigenschaft, die durch ThemeStateProperty.Nameidentifiziert werden kann, den aktuellen Wert zurück, der auf dem Zielhost-GameObject festgelegt ist (d. h. die aktuelle Materialfarbe, der aktuelle lokale Positionsoffset usw.). Dies wird in erster Linie zum Zwischenspeichern des Startwerts verwendet, wenn die Beschleunigung zwischen den Zuständen erfolgt.

public abstract ThemeDefinition 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 der angegebenen ThemePropertyValue-Einstellung, die festgelegt wird, 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 abzielen und die im ThemeDefinition-Parameter definierten Eigenschaften und Konfigurationen verwenden. Es wird empfohlen, am Anfang einer Überschreibung aufzurufen base.Init(host, settings) .

InteractableThemeBase.IsEasingSupported

Wenn die benutzerdefinierte Design-Engine die Beschleunigung zwischen Werten unterstützen kann, die über die ThemeDefinition.Easing -Eigenschaft konfiguriert wird.

InteractableThemeBase.AreShadersSupported

Wenn die benutzerdefinierte Design-Engine Shadereigenschaften unterstützen kann. Es wird empfohlen, von InteractableShaderTheme zu erweitern, um von der vorhandenen Infrastruktur zum effizienten Festlegen/Abrufen von Shadereigenschaften über MaterialPropertyBlocks zu profitieren. Die Shadereigenschafteninformationen werden in jedem ThemeStateProperty über ThemeStateProperty.TargetShader und ThemeStateProperty.ShaderPropertyNamegespeichert.

Hinweis

Wenn Sie erweitern InteractableShaderTheme, kann es auch nützlich sein, die InteractableShaderTheme.DefaultShaderProperty über new zu überschreiben.

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

Darüber hinaus erweitern die folgenden Klassen die InteractableShaderTheme -Klasse, die erneut MaterialPropertyBlocks verwendet, um Shadereigenschaftenwerte zu ändern. Dieser Ansatz trägt zur Leistung bei, da MaterialPropertyBlocks keine neuen instanzierten Materialien erstellt, wenn sich die Werte ändern. Der Zugriff auf die typischen Eigenschaften der Material-Klasse 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

Weist das Design an, alle geänderten Eigenschaften auf ihre ursprünglichen Werte zurückzusetzen, die beim Initialisieren dieser Design-Engine auf dem Host GameObject festgelegt wurden.

Beispiel für benutzerdefinierte Design-Engine

Die folgende Klasse ist ein Beispiel für eine benutzerdefinierte neue Design-Engine. Diese Implementierung sucht eine MeshRenderer-Komponente auf dem initialisierten Hostobjekt und steuert dessen 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 codebeispiel unten zeigt, wie dieses Design zur Laufzeit gesteuert werden kann, um die benutzerdefinierte Design-Engine zu erweitern, die im vorherigen Abschnitt definiert wurde. Insbesondere wird erläutert, wie Sie den aktuellen Zustand für das Design festlegen, damit die MeshRenderer-Sichtbarkeit entsprechend aktualisiert wird.

Hinweis

theme.OnUpdate(state,force) sollte in der Regel in der Update()-Methode aufgerufen werden, um Design-Engines zu unterstützen, die die Beschleunigung/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;
    }
}

Weitere Informationen