Motywy wizualne — MRTK2

Motywy umożliwiają elastyczną kontrolę nad aktywami środowiska użytkownika w odpowiedzi na różne przejścia stanów. Może to obejmować zmianę koloru przycisku, zmianę rozmiaru elementu w odpowiedzi na fokus itp. Struktura Motywy wizualne składa się z dwóch kluczowych elementów: 1) konfiguracji i 2) aparatów środowiska uruchomieniowego.

Konfiguracje motywów to definicje właściwości i typów, podczas gdy aparaty motywów to klasy, które używają konfiguracji i implementują logikę w celu aktualizowania przekształceń, materiałów i nie tylko w czasie wykonywania.

Konfiguracja motywu

Konfiguracje motywów to ScriptableObjects definiujące sposób inicjowania aparatów motywów w czasie wykonywania. Definiują właściwości i wartości, które mają być używane w odpowiedzi na dane wejściowe lub inne zmiany stanu, gdy aplikacja jest uruchomiona. Jako zasoby ScriptableObjects konfiguracje motywów można zdefiniować raz, a następnie ponownie używać w różnych składnikach środowiska użytkownika.

Aby utworzyć nowy Theme zasób:

  1. Kliknij prawym przyciskiem myszy w oknie projektu
  2. Wybierz pozycję Utwórz> Mixed RealityMotywzestawu narzędzi>

Przykładowe zasoby konfiguracji motywu można znaleźć w obszarze MRTK/SDK/Features/UX/Interactable/Themes.

Przykład obiektu ScriptableObject motywu w inspektorze

Stany

Podczas tworzenia nowego Themeelementu pierwszą rzeczą do ustawienia jest to, jakie stany są dostępne. Właściwość States wskazuje, ile wartości musi zdefiniować konfiguracja motywu, ponieważ będzie jedna wartość na stan. Na powyższym przykładowym obrazie domyślne stany zdefiniowane dla składnika z możliwością interakcji to Domyślne, Fokus, Naciśnięcie i Wyłączone. Są one zdefiniowane w DefaultInteractableStates pliku zawartości (Assets/MRTK/SDK/Features/UX/Interactable/States).

Aby utworzyć nowy State zasób:

  1. Kliknij prawym przyciskiem myszy w oknie projektu
  2. Wybierz pozycję Utwórz> Mixed RealityStanzestawu narzędzi>

Przykład obiektu ScriptableObject w inspektorze

Obiekt State ScriptableObject definiuje zarówno listę stanów, jak i typ modelu StateModel do utworzenia dla tych stanów. StateModel to klasa, która rozszerza i implementuje BaseStateModel logikę maszyny stanu w celu wygenerowania bieżącego stanu w czasie wykonywania. Bieżący stan tej klasy jest zwykle używany przez aparaty motywów w czasie wykonywania, aby dyktować, jakie wartości mają być ustawiane względem właściwości materiału, przekształcenia GameObject i nie tylko.

Właściwości aparatu motywu

Poza stanamiTheme zasób definiuje również listę aparatów motywów i skojarzone właściwości dla tych aparatów. Aparat motywu ponownie definiuje logikę ustawiania poprawnych wartości względem obiektu GameObject w czasie wykonywania.

Zasób Theme może zdefiniować wiele aparatów motywów w celu osiągnięcia zaawansowanych przejść stanów wizualnych przeznaczonych dla wielu właściwości obiektu GameObject.

Środowisko uruchomieniowe motywu

Definiuje typ klasy aparatu motywu, który zostanie utworzony

Dynamiki

Niektóre aparaty motywów, jeśli definiują swoją właściwość IsEasingSupported jako true, obsługują złagodzenie między stanami. Na przykład lerping między dwoma kolorami po zmianie stanu. Czas trwania definiuje w sekundach, jak długo można ułatwić od wartości początkowej do wartości końcowej, a krzywa animacji definiuje szybkość zmian w tym okresie.

Właściwości cieniowania

Niektóre aparaty motywów, jeśli definiują swoją właściwość AreShadersSupported jako true, zmodyfikują określone właściwości cieniowania w czasie wykonywania. Pola Cieniowanie i właściwość definiują właściwość cieniowania na wartość docelową.

Tworzenie konfiguracji motywu za pomocą kodu

Ogólnie rzecz biorąc, łatwiej jest zaprojektować konfiguracje motywu za pośrednictwem inspektora aparatu Unity, ale istnieją przypadki, w których motywy muszą być dynamicznie generowane w czasie wykonywania za pomocą kodu. Poniższy fragment kodu przedstawia przykład wykonania tego zadania.

Aby ułatwić tworzenie aplikacji, poniższe metody pomocnika są przydatne w celu uproszczenia konfiguracji.

Interactable.GetDefaultInteractableStates() — tworzy nowy obiekt States ScriptableObject z czterema domyślnymi wartościami stanu używanymi w składniku Interakcja .

ThemeDefinition.GetDefaultThemeDefinition<T>() — Każdy aparat motywu definiuje konfigurację domyślną z odpowiednimi właściwościami wymaganymi dla tego typu środowiska uruchomieniowego motywu. Ten pomocnik tworzy definicję dla danego typu aparatu motywu.

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

Aparaty motywów

Aparat motywu to klasa, która rozciąga się od InteractableThemeBase klasy. Te klasy są tworzone w czasie wykonywania i konfigurowane za pomocą obiektu zgodnie z wcześniejszym opisem ThemeDefinition .

Domyślne aparaty motywów

Zestaw NARZĘDZI MRTK jest dostarczany z domyślnym zestawem aparatów motywów wymienionych poniżej:

Domyślne aparaty motywów można znaleźć w obszarze MRTK/SDK/Features/UX/Scripts/VisualThemes/ThemeEngines.

Niestandardowe aparaty motywów

Jak wspomniano, aparat motywu jest definiowany jako klasa, która rozciąga się od InteractableThemeBase klasy. W związku z tym nowy aparat motywów potrzebuje tylko rozszerzenia tej klasy i zaimplementowania następujących elementów:

Obowiązkowe implementacje

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

Dla danej właściwości, która może być identyfikowana przez ThemeStateProperty.Name, ustaw jego bieżącą wartość stanu na docelowym hoście GameObject (tj. ustawić kolor materiału itp.). Indeks wskazuje bieżącą wartość stanu, aby uzyskać dostęp do wartości i wartość procentową, zmienną z zakresu od 0 do 1, jest używana do złagodzenia/lerpingu między wartościami.

public abstract ThemePropertyValue GetProperty(ThemeStateProperty property)

Dla danej właściwości, którą można zidentyfikować za pomocą ThemeStateProperty.Namemetody , zwróć bieżącą wartość ustawioną na docelowym obiekcie GameObject hosta (tj. bieżący kolor materiału, przesunięcie bieżącej pozycji lokalnej itp.). Jest to używane głównie do buforowania wartości początkowej podczas złagodzenia między stanami.

public abstract ThemeDefinition GetDefaultThemeDefinition()

ThemeDefinition Zwraca obiekt, który definiuje domyślne właściwości i konfigurację wymaganą dla motywu niestandardowego

protected abstract void SetValue(ThemeStateProperty property, ThemePropertyValue value)

Chroniony wariant definicji publicznej SetValue() , z wyjątkiem podanego elementu ThemePropertyValue do ustawienia zamiast kierowania do używania konfiguracji indeksu i/lub wartości procentowej.

InteractableThemeBase.Init(GameObject host, ThemeDefinition settings)

Wykonaj wszystkie kroki inicjowania w tym miejscu przeznaczone dla podanego parametru GameObject i przy użyciu właściwości i konfiguracji zdefiniowanych w parametrze ThemeDefinition . Zaleca się wywołanie base.Init(host, settings) na początku przesłonięcia.

InteractableThemeBase.IsEasingSupported

Jeśli niestandardowy aparat motywu może obsługiwać złagodzenie między wartościami skonfigurowanymi za pośrednictwem ThemeDefinition.Easing właściwości.

InteractableThemeBase.AreShadersSupported

Jeśli niestandardowy aparat motywu może obsługiwać właściwości cieniowania. Zaleca się rozszerzenie możliwości korzystania z InteractableShaderTheme istniejącej infrastruktury w celu wydajnego ustawiania/uzyskiwania właściwości cieniowania za pośrednictwem właściwości MaterialPropertyBlocks. Informacje o właściwości cieniowania są przechowywane w poszczególnych ThemeStateProperty elementach za pośrednictwem i ThemeStateProperty.TargetShaderThemeStateProperty.ShaderPropertyName.

Uwaga

W przypadku rozszerzenia InteractableShaderThememożna również zastąpić właściwość InteractableShaderTheme.DefaultShaderProperty za pośrednictwem nowej.

Przykładowy kod: protected new const string DefaultShaderProperty = "_Color";

Ponadto poniższe klasy rozszerzają klasę InteractableShaderTheme , która ponownie używa właściwości MaterialPropertyBlocks do modyfikowania wartości właściwości cieniowania. Takie podejście pomaga w wydajności , ponieważ obiekty MaterialPropertyBlock nie tworzą nowych materiałów wystąpionych po zmianie wartości. Jednak uzyskiwanie dostępu do typowych właściwości klasy Material nie zwróci oczekiwanych wartości. Użyj właściwości MaterialPropertyBlocks , aby uzyskać i zweryfikować bieżące wartości właściwości materiału (tj. _Color lub _MainTex).

InteractableThemeBase.Reset

Kieruje motyw do resetowania wszelkich zmodyfikowanych właściwości z powrotem do oryginalnych wartości ustawionych na hoście GameObject podczas inicjowania tego aparatu motywu.

Przykład aparatu motywu niestandardowego

Poniższa klasa jest przykładem niestandardowego nowego aparatu motywu. Ta implementacja znajdzie składnik MeshRenderer na zainicjowanym obiekcie hosta i kontroluje jego widoczność na podstawie bieżącego stanu.

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

Kompleksowy przykład

Rozszerzenie niestandardowego aparatu motywu zdefiniowanego we wcześniejszej sekcji, w poniższym przykładzie kodu pokazano, jak kontrolować ten motyw w czasie wykonywania. W szczególności sposób ustawiania bieżącego stanu motywu tak, aby widoczność meshRenderer została odpowiednio zaktualizowana.

Uwaga

theme.OnUpdate(state,force) powinna być zwykle wywoływana w metodzie Update() w celu obsługi aparatów motywów korzystających z złagodzenia/lerpingu między wartościami.

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

Zobacz też