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:

  1. Kliknutí pravým tlačítkem v okně projektu
  2. Vyberte Vytvořit>Mixed Reality Motiv sady nástrojů>.

Příklad prostředků konfigurace motivu najdete v části MRTK/SDK/Features/UX/Interactable/Themes.

Příklad motivu ScriptableObject v inspektoru

Stavy

Při vytváření nového Themeje 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:

  1. Kliknutí pravým tlačítkem v okně projektu
  2. Vyberte Vytvořit>Mixed Reality Stav sady nástrojů>.

Příklad States ScriptableObject v inspektoru

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:

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.

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.

InteractableThemeBase.Reset

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

Viz také