Motywy wizualne

Motywy umożliwiają elastyczną kontrolę nad zasobami ś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. Środowisko motywów wizualnych 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, natomiast aparaty motywów to klasy, które zużywają konfiguracje i implementują logikę aktualizowania przekształceń, materiałów i innych elementów w czasie wykonywania.

Konfiguracja motywu

Konfiguracje motywów to scriptableObjects definiujące sposób inicjowania aparatów motywów w czasie wykonywania. Definiują one właściwości i wartości do wykorzystania w odpowiedzi na zmiany danych wejściowych lub innych zmian 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 Project aplikacji
  2. Wybierz pozycję Utwórz Mixed Reality > zestawu narzędzi >

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

Przykład ScriptableObject motywu w inspektorze

Stany

Podczas tworzenia nowego Theme zestawu pierwszą rzeczą do ustawienia są stany, które 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ślnymi stanami zdefiniowanymi dla składnika W interakcje są Domyślne, Fokus, Naciśnięty i Wyłączony. Są one zdefiniowane w pliku DefaultInteractableStates zasobów (Assets/MRTK/SDK/Features/UX/Interactable/States).

Aby utworzyć nowy State zasób:

  1. Kliknij prawym przyciskiem myszy w oknie Project aplikacji
  2. Wybierz pozycję Utwórz Mixed Reality stanu > zestawu > narzędzi

Przykład States ScriptableObject w inspektorze

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

Właściwości aparatu motywów

Poza stanami zasób definiuje również listę aparatów Theme motywów i skojarzonych właściwości dla tych aparatów. Aparat motywów ponownie definiuje logikę, aby ustawić poprawne wartości względem gameObject w czasie wykonywania.

Zasób Theme może definiować 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ą easing między stanami. Na przykład tolerancja między dwoma kolorami w przypadku zmiany stanu. Czas trwania określa w sekundach, jak długo ma być łatwość od wartości początku 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 zdefiniują swoją właściwość AreShadersSupported jako true, zmodyfikują konkretne właściwości modułu cieniującego w czasie wykonywania. Pola Shader (Cieniowanie) i Property (Właściwość) definiują właściwość cieniowania jako docelową.

Tworzenie konfiguracji motywu za pomocą kodu

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

Aby przyspieszyć opracowywanie, następujące metody pomocnika są przydatne w celu uproszczenia instalacji.

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

ThemeDefinition.GetDefaultThemeDefinition<T>() — Każdy aparat motywów definiuje konfigurację domyślną z prawidłowymi 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 motywów to klasa, która rozciąga się od InteractableThemeBase klasy . Te klasy są konfigurowane w czasie wykonywania i konfigurowane przy użyciu ThemeDefinition obiektu zgodnie z wcześniejszymi wspomniano.

Domyślne aparaty motywów

Zestaw MRTK jest dostarczany z domyślnym zestawem aparatów motywów wymienionym poniżej:

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

Aparaty motywów niestandardowych

Jak dano, aparat motywów jest definiowany jako klasa, która rozciąga się od InteractableThemeBase klasy . W związku z tym nowy aparat motywów musi rozszerzyć tylko tę klasę i zaimplementować następujące elementy:

Obowiązkowe implementacje

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

Dla danej właściwości, którą można zidentyfikować za pomocą , ustaw jej wartość bieżącego stanu na docelowym hoście ThemeStateProperty.Name GameObject (tj. ustaw kolor materiału itp.). Indeks wskazuje wartość bieżącego stanu do uzyskania dostępu, a wartość procentowa , zmiennoprzecinkowa z wartości od 0 do 1, jest używana do easingu/lerowania między wartościami.

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

Dla danej właściwości, którą można zidentyfikować za pomocą , zwróć bieżącą wartość ustawioną dla docelowego obiektu ThemeStateProperty.Name GameObject hosta (tj. bieżący kolor materiału, przesunięcie bieżącej pozycji lokalnej itp.). Jest on używany głównie do buforowania wartości rozpoczęcia podczas easingu między stanami.

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

Zwraca obiekt definiujący domyślne właściwości i konfigurację ThemeDefinition wymagane dla motywu niestandardowego

protected abstract void SetValue(ThemeStateProperty property, ThemePropertyValue value)

Chroniony wariant definicji publicznej, z wyjątkiem opcji ThemePropertyValue, która ma być ustawiona zamiast kierowania do używania konfiguracji indeksu SetValue() i/lub wartości procentowej.

InteractableThemeBase.Init(GameObject host, ThemeDefinition settings)

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

InteractableThemeBase.IsEasingSupported

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

InteractableThemeBase.AreShadersSupported

Jeśli niestandardowy aparat motywów może obsługiwać właściwości cieniowania określania wartości docelowej. Zaleca się rozszerzenie programu z programu w celu skorzystania z istniejącej infrastruktury w celu wydajnego ustawienia/uzyskania właściwości modułu cieniującego za InteractableShaderTheme pośrednictwem obiektu MaterialPropertyBlocks. Informacje o właściwościach cieniowania są przechowywane w każdej z nich ThemeStateProperty za pośrednictwem ThemeStateProperty.TargetShader i ThemeStateProperty.ShaderPropertyName .

Uwaga

W przypadku rozszerzania funkcji przydatne może być również zastąpienie właściwości InteractableShaderTheme InteractableShaderTheme.DefaultShaderProperty za pośrednictwem nowego .

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

Ponadto poniższe klasy rozszerzają klasę InteractableShaderTheme , która ponownie używa obiektu MaterialPropertyBlocks do modyfikowania wartości właściwości cieniowania. Takie podejście pomaga zwiększyć wydajność, ponieważ bloki MaterialPropertyBlocks nie tworzą nowych wystąpień materiałów, gdy wartości zmieniają się. Jednak uzyskanie dostępu do typowych właściwości klasy Material nie zwróci oczekiwanych wartości. Użyj bloków MaterialPropertyBlocks, aby pobrać i zweryfikować bieżące wartości właściwości materiału (tj. _Color lub _MainTex).

InteractableThemeBase.Reset

Kieruje motyw do resetowania wszystkich zmodyfikowanych właściwości z powrotem do ich oryginalnych wartości, które zostały ustawione na hoście GameObject podczas inicjowania tego aparatu motywu.

Przykład aparatu motywów niestandardowych

Klasa poniżej jest przykładem niestandardowego nowego aparatu motywów. Ta implementacja znajdzie składnik MeshRenderer w inicjalizowanym obiekcie hosta i będzie kontrolować 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 motywów zdefiniowanego we wcześniejszej sekcji w poniższym przykładzie kodu pokazuje, jak kontrolować ten motyw w czasie wykonywania. W szczególności sposób ustawienia bieżącego stanu w motywie w celu odpowiedniego zaktualizowania widoczności meshRenderer.

Uwaga

theme.OnUpdate(state,force) Metoda Update() powinna być zwykle wywoływana w celu obsługi aparatów motywów, które wykorzystują easing/lerping 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ż