Visuele thema's — MRTK2

Thema's maken flexibel beheer van UX-assets mogelijk als reactie op verschillende statusovergangen. Dit kan nodig zijn om de kleur van een knop te wijzigen, het formaat van een element te wijzigen als reactie op focus, enzovoort. Het framework Visual Themes bestaat uit twee belangrijke onderdelen: 1) configuratie en 2) runtime-engines.

Themaconfiguraties zijn definities van eigenschappen en typen, terwijl thema-engines klassen zijn die de configuraties gebruiken en de logica implementeren om transformaties, materialen en meer bij runtime bij te werken.

Themaconfiguratie

Themaconfiguraties zijn ScriptableObjects die bepalen hoe Thema-engines tijdens runtime worden geïnitialiseerd. Ze definiëren welke eigenschappen en waarden moeten worden gebruikt als reactie op invoer of andere statuswijzigingen wanneer de app wordt uitgevoerd. Als ScriptableObjects-assets kunnen themaconfiguraties eenmaal worden gedefinieerd en vervolgens opnieuw worden gebruikt in verschillende UX-onderdelen.

Een nieuwe Theme asset maken:

  1. Klik met de rechtermuisknop in het projectvenster
  2. Selecteer Create>Mixed Reality Toolkit>Theme

Voorbeelden van themaconfiguratieassets vindt u onder MRTK/SDK/Features/UX/Interactable/Themes.

Voorbeeld van themaScriptableObject in inspector

Staten

Wanneer u een nieuwe Thememaakt, moet u eerst instellen welke statussen beschikbaar zijn. De eigenschap Staten geeft aan hoeveel waarden een themaconfiguratie moet definiëren, omdat er één waarde per status is. In de bovenstaande voorbeeldafbeelding zijn de standaardstatussen die zijn gedefinieerd voor het onderdeel Interactie mogelijkStandaard, Focus, Ingedrukt en Uitgeschakeld. Deze worden gedefinieerd in het DefaultInteractableStates assetbestand (Assets/MRTK/SDK/Features/UX/Interactable/States).

Een nieuwe State asset maken:

  1. Klik met de rechtermuisknop in het projectvenster
  2. Selecteer Create>Mixed Reality Toolkit>State

Statussen ScriptableObject-voorbeeld in Inspector

Een State ScriptableObject definieert zowel de lijst met statussen als het type StateModel dat voor deze statussen moet worden gemaakt. Een StateModel is een klasse die de statuscomputerlogica uitbreidt BaseStateModel en implementeert om de huidige status tijdens runtime te genereren. De huidige status van deze klasse wordt over het algemeen gebruikt door thema-engines tijdens runtime om te bepalen welke waarden moeten worden ingesteld op basis van materiaaleigenschappen, GameObject-transformaties en meer.

Eigenschappen van thema-engine

Buiten staten definieert een Theme asset ook een lijst met thema-engines en de bijbehorende eigenschappen voor deze engines. Een thema-engine definieert opnieuw de logica voor het instellen van de juiste waarden voor een GameObject tijdens runtime.

Een Theme asset kan meerdere thema-engines definiëren om geavanceerde overgangen van visuele statussen te bereiken die zijn gericht op meerdere GameObject-eigenschappen.

Themaruntime

Definieert het klassetype van de thema-engine die wordt gemaakt

Versoepeling

Als sommige thema-engines hun eigenschap IsEasingSupported definiëren als waar, ondersteunen ze versoepeling tussen staten. Bijvoorbeeld lerping tussen twee kleuren wanneer een statuswijziging plaatsvindt. De Duur definieert in seconden hoe lang u van beginwaarde naar eindwaarde moet versoepelen en de Animatiecurve definieert de snelheid van wijziging tijdens die periode.

Arceringseigenschappen

Als sommige thema-engines de eigenschap AreShadersSupported als true definiëren, worden bepaalde arceringseigenschappen tijdens runtime gewijzigd. De velden Shader en Eigenschap definiëren de arceringseigenschap die u wilt gebruiken.

Een themaconfiguratie maken via code

Over het algemeen is het eenvoudiger om themaconfiguraties te ontwerpen via de Unity-inspector, maar er zijn gevallen waarin thema's dynamisch moeten worden gegenereerd tijdens runtime via code. Het onderstaande codefragment bevat een voorbeeld van hoe u deze taak kunt uitvoeren.

Om de ontwikkeling te versnellen, zijn de volgende helpermethoden handig voor het vereenvoudigen van de installatie.

Interactable.GetDefaultInteractableStates()- maakt een nieuw StatusScriptableObject met de vier standaardstatuswaarden die worden gebruikt in het onderdeel Interactie.

ThemeDefinition.GetDefaultThemeDefinition<T>() - Elke thema-engine definieert een standaardconfiguratie met de juiste eigenschappen die nodig zijn voor dat themaruntimetype. Met deze helper maakt u een definitie voor het opgegeven type Thema-engine.

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

Thema-engines

Een thema-engine is een klasse die zich uitstrekt van de InteractableThemeBase klasse. Deze klassen worden geïnstantieerd tijdens runtime en geconfigureerd met een ThemeDefinition object zoals eerder beschreven.

Standaardthema-engines

MRTK wordt geleverd met een standaardset thema-engines die hieronder worden vermeld:

De standaardthema-engines vindt u onder MRTK/SDK/Features/UX/Scripts/VisualThemes/ThemeEngines.

Aangepaste thema-engines

Zoals vermeld, wordt een thema-engine gedefinieerd als een klasse die zich uitbreidt van de InteractableThemeBase klasse. De nieuwe thema-engine hoeft dus alleen deze klasse uit te breiden en het volgende te implementeren:

Verplichte implementaties

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

Voor de opgegeven eigenschap, die kan worden geïdentificeerd door ThemeStateProperty.Name, stelt u de huidige statuswaarde in op de doel-GameObject-host (d.w.z. de materiaalkleur instellen, enzovoort). De index geeft de huidige statuswaarde aan die moet worden geopend en het percentage, een float tussen 0 en 1, wordt gebruikt voor versoepeling/tolerantie tussen waarden.

public abstract ThemePropertyValue GetProperty(ThemeStateProperty property)

Voor de opgegeven eigenschap, die kan worden geïdentificeerd door ThemeStateProperty.Name, retourneert u de huidige waarde die is ingesteld op het doelhost-GameObject (d.w.w.v. de huidige materiaalkleur, de huidige verschuiving van de lokale positie, enzovoort). Dit wordt voornamelijk gebruikt voor het opslaan in de cache van de beginwaarde bij het versoepelen tussen statussen.

public abstract ThemeDefinition GetDefaultThemeDefinition()

Retourneert een ThemeDefinition object dat de standaardeigenschappen en configuratie definieert die nodig zijn voor het aangepaste thema

protected abstract void SetValue(ThemeStateProperty property, ThemePropertyValue value)

Beveiligde variant van de openbare SetValue() definitie, met uitzondering van opgegeven ThemePropertyValue om in te stellen in plaats van om index- en/of percentageconfiguratie te gebruiken.

InteractableThemeBase.Init(GameObject host, ThemeDefinition settings)

Voer hier eventuele initialisatiestappen uit die gericht zijn op de opgegeven GameObject-parameter en gebruik de eigenschappen en configuraties die zijn gedefinieerd in de parameter ThemeDefinition . Het wordt aanbevolen om aan te roepen base.Init(host, settings) aan het begin van een onderdrukking.

InteractableThemeBase.IsEasingSupported

Of de aangepaste thema-engine ondersteuning kan bieden voor versoepeling tussen waarden die via de ThemeDefinition.Easing eigenschap zijn geconfigureerd.

InteractableThemeBase.AreShadersSupported

Als de aangepaste thema-engine ondersteuning kan bieden voor arceringseigenschappen. Het wordt aanbevolen om uit InteractableShaderTheme te breiden van om te profiteren van de bestaande infrastructuur om efficiënt arceringseigenschappen in te stellen/te verkrijgen via MaterialPropertyBlocks. De informatie over de arceringseigenschap wordt opgeslagen in elke ThemeStateProperty via ThemeStateProperty.TargetShader en ThemeStateProperty.ShaderPropertyName.

Notitie

Als u uitbreidt InteractableShaderTheme, kan het ook handig zijn om de InteractableShaderTheme.DefaultShaderProperty via nieuw te overschrijven.

Voorbeeldcode: protected new const string DefaultShaderProperty = "_Color";

Bovendien breiden de volgende klassen hieronder de InteractableShaderTheme klasse uit die opnieuw MaterialPropertyBlocks gebruikt om arceringseigenschapswaarden te wijzigen. Deze benadering helpt de prestaties omdat MaterialPropertyBlocks geen nieuw exemplaarmateriaal maakt wanneer waarden veranderen. Het openen van de typische eigenschappen van de materiaalklasse retourneert echter geen verwachte waarden. Gebruik MaterialPropertyBlocks om huidige materiaaleigenschapswaarden ( dat wil _Color of _MainTex) op te halen en te valideren.

InteractableThemeBase.Reset

Hiermee wordt het thema om gewijzigde eigenschappen terug te zetten naar de oorspronkelijke waarden die zijn ingesteld op de host GameObject toen deze thema-engine werd geïnitialiseerd.

Voorbeeld van aangepaste thema-engine

De onderstaande klasse is een voorbeeld van een aangepaste nieuwe thema-engine. Deze implementatie vindt een MeshRenderer-onderdeel op het geïnitialiseerde hostobject en bepaalt de zichtbaarheid ervan op basis van de huidige status.

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

Voorbeeld van het hele proces

In het onderstaande codevoorbeeld ziet u hoe u dit thema tijdens runtime kunt beheren, naast de aangepaste thema-engine die in de eerdere sectie is gedefinieerd. In het bijzonder hoe u de huidige status van het thema instelt, zodat de zichtbaarheid van MeshRenderer op de juiste manier wordt bijgewerkt.

Notitie

theme.OnUpdate(state,force) moet over het algemeen worden aangeroepen in de methode Update() ter ondersteuning van Theme Engines die gebruikmaken van versoepeling/lerping tussen waarden.

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

Zie ook