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:
- Kliknij prawym przyciskiem myszy w oknie projektu
- Wybierz pozycję Utwórz> Mixed RealityMotywzestawu narzędzi>
Przykładowe zasoby konfiguracji motywu można znaleźć w obszarze MRTK/SDK/Features/UX/Interactable/Themes
.
Stany
Podczas tworzenia nowego Theme
elementu 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:
- Kliknij prawym przyciskiem myszy w oknie projektu
- Wybierz pozycję Utwórz> Mixed RealityStanzestawu narzędzi>
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:
InteractableActivateTheme
InteractableAnimatorTheme
InteractableAudioTheme
InteractableColorChildrenTheme
InteractableColorTheme
InteractableGrabScaleTheme
InteractableMaterialTheme
InteractableOffsetTheme
InteractableRotationTheme
InteractableScaleTheme
InteractableShaderTheme
InteractableStringTheme
InteractableTextureTheme
ScaleOffsetColorTheme
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.Name
metody , 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.
Zalecane zastąpienia
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.TargetShader
ThemeStateProperty.ShaderPropertyName
.
Uwaga
W przypadku rozszerzenia InteractableShaderTheme
moż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).
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;
}
}