Vizuální motivy – MRTK2
Motivy umožňují flexibilní řízení prostředků uživatelského prostředí v reakci na přechody různých stavů. To může zahrnovat změnu barvy tlačítka, změnu velikosti prvku v reakci na fokus atd. Architektura Visual Themes se skládá ze dvou klíčových částí: 1) konfigurace a 2) moduly runtime.
Konfigurace motivu jsou definice vlastností a typů, zatímco moduly motivů jsou třídy, které využívají konfigurace a implementují logiku pro aktualizaci transformací, materiálů a dalších za běhu.
Konfigurace motivu
Konfigurace motivu jsou ScriptableObjects , které definují způsob inicializace modulů motivů za běhu. Definují, jaké vlastnosti a hodnoty se mají použít v reakci na změny vstupu nebo jiného stavu, když je aplikace spuštěná. Jako prostředky ScriptableObjects je možné konfigurace motivu definovat jednou a pak znovu použít v různých komponentách uživatelského prostředí.
Vytvoření nového Theme
prostředku:
- Kliknutí pravým tlačítkem v okně projektu
- Vyberte Vytvořit>Mixed Reality Motiv sady nástrojů>.
Příklad prostředků konfigurace motivu najdete v části MRTK/SDK/Features/UX/Interactable/Themes
.
Stavy
Při vytváření nového Theme
je potřeba nejprve nastavit, jaké stavy jsou k dispozici.
Vlastnost States určuje, kolik hodnot musí konfigurace motivu definovat, protože pro každý stav bude existovat jedna hodnota. Na výše uvedeném obrázku jsou výchozí stavy definované pro interagovatelnýkomponentu Výchozí, Fokus, Stisknuto a Zakázáno. Ty jsou definované v souboru assetu DefaultInteractableStates
(Assets/MRTK/SDK/Features/UX/Interactable/States).
Vytvoření nového State
prostředku:
- Kliknutí pravým tlačítkem v okně projektu
- Vyberte Vytvořit>Mixed Reality Stav sady nástrojů>.
ScriptableObject State
definuje seznam stavů i typ StateModel , který se má pro tyto stavy vytvořit.
StateModel je třída, která rozšiřuje BaseStateModel
a implementuje logiku stavového počítače pro generování aktuálního stavu za běhu. Aktuální stav z této třídy je obecně používán moduly motivů za běhu k diktování, jaké hodnoty se mají nastavit proti vlastnostem materiálu, transformace GameObject a další.
Vlastnosti modulu motivů
Mimo státyTheme
definuje prostředek také seznam modulů motivů a přidružené vlastnosti pro tyto moduly.
Modul motivu znovu definuje logiku pro nastavení správných hodnot proti GameObject za běhu.
Prostředek Theme
může definovat více modulů motivů, aby bylo možné dosáhnout sofistikovaných přechodů vizuálních stavů, jejichž cílem je více vlastností GameObject.
Modul runtime motivu
Definuje typ třídy modulu motivu, který bude vytvořen.
Uvolňování
Některé moduly motivů, pokud definují vlastnost IsEasingSupported jako true, podporují uvolňování mezi stavy. Při změně stavu můžete například přepístit mezi dvěma barvami. Doba trvání definuje v sekundách, jak dlouho se má od počáteční hodnoty do koncové hodnoty uvolnit, a křivka animace definuje rychlost změn během tohoto časového období.
Vlastnosti shaderu
Některé moduly motivů, pokud definují svou vlastnost AreShadersSupported jako true, upraví konkrétní vlastnosti shaderu za běhu. Pole Shader a Property definují vlastnost shaderu pro cíl.
Vytvoření konfigurace motivu prostřednictvím kódu
Obecně je jednodušší navrhnout konfigurace motivu prostřednictvím inspektoru Unity, ale existují případy, kdy se motivy musí dynamicky generovat za běhu prostřednictvím kódu. Následující fragment kódu poskytuje příklad, jak tuto úlohu provést.
Pro urychlení vývoje jsou užitečné následující pomocné metody pro zjednodušení nastavení.
Interactable.GetDefaultInteractableStates()
– vytvoří nový States ScriptableObject se čtyřmi výchozími hodnotami stavu použitými v komponentě Interactable .
ThemeDefinition.GetDefaultThemeDefinition<T>()
– Každý modul motivů definuje výchozí konfiguraci se správnými vlastnostmi potřebnými pro daný typ modulu runtime motivu. Tento pomocník vytvoří definici pro daný typ modulu motivu.
// 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 };
Moduly motivů
Modul motivu je třída, která se rozšiřuje z InteractableThemeBase
třídy. Tyto třídy se vytvářejí za běhu a konfigurují s objektem ThemeDefinition
, jak je popsáno výše.
Výchozí moduly motivů
MRTK se dodává s výchozí sadou motivových motorů uvedených níže:
InteractableActivateTheme
InteractableAnimatorTheme
InteractableAudioTheme
InteractableColorChildrenTheme
InteractableColorTheme
InteractableGrabScaleTheme
InteractableMaterialTheme
InteractableOffsetTheme
InteractableRotationTheme
InteractableScaleTheme
InteractableShaderTheme
InteractableStringTheme
InteractableTextureTheme
ScaleOffsetColorTheme
Výchozí moduly motivů najdete v části MRTK/SDK/Features/UX/Scripts/VisualThemes/ThemeEngines
.
Vlastní moduly motivů
Jak je uvedeno, modul motivů je definován jako třída, která se rozšiřuje z InteractableThemeBase
třídy. Proto nový modul motivů potřebuje pouze rozšířit tuto třídu a implementovat následující:
Povinné implementace
public abstract void SetValue(ThemeStateProperty property, int index, float percentage)
Pro danou vlastnost, kterou lze identifikovat pomocí ThemeStateProperty.Name
, nastavte hodnotu aktuálního stavu na cílovém hostiteli GameObject (tj. nastavte barvu materiálu atd.).
Index označuje hodnotu aktuálního stavu, ke které se má přistupovat, a procento, float mezi 0 a 1, se používá k uvolnění nebo přecházení mezi hodnotami.
public abstract ThemePropertyValue GetProperty(ThemeStateProperty property)
Pro danou vlastnost, kterou lze identifikovat pomocí ThemeStateProperty.Name
, vraťte aktuální hodnotu nastavenou na cílovém objektu Host GameObject (tj. aktuální barvu materiálu, aktuální posun místní pozice atd.). Primárně se používá k ukládání počáteční hodnoty do mezipaměti při uvolňování mezi stavy.
public abstract ThemeDefinition GetDefaultThemeDefinition()
ThemeDefinition
Vrátí objekt, který definuje výchozí vlastnosti a konfiguraci potřebné pro vlastní motiv.
protected abstract void SetValue(ThemeStateProperty property, ThemePropertyValue value)
Chráněná varianta veřejné SetValue()
definice s výjimkou poskytnutého ThemePropertyValue k nastavení místo nasměrování na použití indexu a/nebo procentuální konfigurace.
Doporučená přepsání
InteractableThemeBase.Init(GameObject host, ThemeDefinition settings)
Zde proveďte všechny inicializační kroky, které cílí na zadaný parametr GameObject a pomocí vlastností a konfigurací definovaných v parametru ThemeDefinition . Doporučujeme volat base.Init(host, settings)
na začátku přepsání.
InteractableThemeBase.IsEasingSupported
Pokud vlastní modul motivů může podporovat uvolňování mezi hodnotami, které je nakonfigurováno prostřednictvím ThemeDefinition.Easing
vlastnosti.
InteractableThemeBase.AreShadersSupported
Pokud vlastní modul motivů podporuje cílení na vlastnosti shaderu. Doporučuje se rozšířit InteractableShaderTheme
od stávající infrastruktury a efektivně nastavit nebo získat vlastnosti shaderu prostřednictvím MaterialPropertyBlocks. Informace o vlastnosti shaderu jsou uloženy v každé ThemeStateProperty
přes ThemeStateProperty.TargetShader
a ThemeStateProperty.ShaderPropertyName
.
Poznámka
Pokud rozšiřujete InteractableShaderTheme
, může být také užitečné přepsat InteractableShaderTheme.DefaultShaderProperty prostřednictvím nové.
Příklad kódu: protected new const string DefaultShaderProperty = "_Color";
Dále následující třídy rozšiřují InteractableShaderTheme
třídu, která znovu používá MaterialPropertyBlocks k úpravě hodnot vlastností shaderu. Tento přístup pomáhá výkonu , protože MaterialPropertyBlocks nevytvoří nové instanční materiály při změně hodnot. Přístup k typickým vlastnostem třídy Material však nevrátí očekávané hodnoty. K získání a ověření aktuálních hodnot vlastností materiálu (tj. _Color nebo _MainTex) použijte MaterialPropertyBlocks.
Nasměruje motiv na resetování všech upravených vlastností zpět na původní hodnoty, které byly nastaveny na hostitelském objektu GameObject při inicializaci tohoto modulu motivu.
Příklad vlastního modulu motivů
Následující třída je příkladem vlastního nového modulu motivů. Tato implementace najde komponentu MeshRenderer na inicializovaném hostitelském objektu a řídí její viditelnost na základě aktuálního stavu.
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;
}
}
Kompletní příklad
Následující příklad kódu, který rozšiřuje vlastní modul motivů definovaný v předchozí části, ukazuje, jak řídit tento motiv za běhu. Konkrétně se jedná o to, jak nastavit aktuální stav motivu, aby se viditelnost MeshRenderer náležitě aktualizovala.
Poznámka
theme.OnUpdate(state,force)
Obecně by měla být volána v metodě Update(), aby podporovala moduly motivů, které využívají uvolňování alerování mezi hodnotami.
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;
}
}