Visuella teman

Teman möjliggör flexibel kontroll av UX-tillgångar som svar på olika tillståndsövergångar. Detta kan innebära att ändra en knapps färg, ändra storlek på ett element som svar på fokus osv. Ramverket visuella teman består av två viktiga delar: 1) konfiguration och 2) körningsmotorer.

Temakonfigurationer är definitioner av egenskaper och typer medan temamotorer är klasser som använder konfigurationerna och implementerar logiken för att uppdatera transformeringar, material med mera vid körning.

Temakonfiguration

Temakonfigurationer är ScriptableObjects som definierar hur temamotorer initieras vid körning. De definierar vilka egenskaper och värden som ska användas som svar på indata eller andra tillståndsändringar när appen körs. Som ScriptableObjects-tillgångar kan temakonfigurationer definieras en gång och sedan återanvändas för olika UX-komponenter.

Så här skapar du en ny Theme tillgång:

  1. Högerklicka i fönstret Project
  2. Välj Skapa>Mixed Reality ToolkitTheme>

Exempel på temakonfigurationstillgångar finns under MRTK/SDK/Features/UX/Interactable/Themes.

Theme ScriptableObject example in inspector

Tillstånd

När du skapar en ny Themeär det första att ange vilka tillstånd som är tillgängliga. Egenskapen States anger hur många värden som en temakonfiguration måste definiera eftersom det kommer att finnas ett värde per tillstånd. I exempelbilden ovan är standardtillstånden som definierats för komponenten Interaktionsbarstandard, Fokus, Tryck ochInaktiverad. Dessa definieras i DefaultInteractableStates tillgångsfilen (Assets/MRTK/SDK/Features/UX/Interactable/States).

Så här skapar du en ny State tillgång:

  1. Högerklicka i fönstret Project
  2. Välj Skapa>Mixed Reality ToolkitState>

States ScriptableObject example in inspector

En State ScriptableObject definierar både listan över tillstånd och typen av StateModel som ska skapas för dessa tillstånd. En StateModel är en klass som utökar BaseStateModel och implementerar tillståndsdatorlogik för att generera det aktuella tillståndet vid körning. Det aktuella tillståndet från den här klassen används vanligtvis av temamotorer vid körning för att diktera vilka värden som ska anges mot materialegenskaper, GameObject-transformeringar med mera.

Egenskaper för temamotor

Utanför Delstater definierar en Theme tillgång också en lista över temamotorer och tillhörande egenskaper för dessa motorer. En temamotor definierar återigen logiken för att ange rätt värden mot en GameObject vid körning.

En Theme tillgång kan definiera flera temamotorer för att uppnå avancerade visuella tillståndsövergångar som riktar sig till flera GameObject-egenskaper.

Temakörning

Definierar klasstypen för temamotorn som ska skapas

Lätta

Vissa temamotorer, om de definierar sin egenskap IsEasingSupported som sant, stöder lättnader mellan tillstånd. Exempel: lerping mellan två färger när en tillståndsändring inträffar. Varaktigheten definierar i sekunder hur länge du ska lätta från startvärde till slutvärde och animeringskurvan definierar ändringshastigheten under den tidsperioden.

Skuggningsegenskaper

Vissa temamotorer, om de definierar sin egenskap AreShadersSupported som true, ändrar vissa skuggningsegenskaper vid körning. Fälten Shader och Property definierar skuggningsegenskapen till mål.

Skapa en temakonfiguration via kod

I allmänhet är det enklare att utforma temakonfigurationer via Unity-inspektören, men det finns fall där teman måste genereras dynamiskt vid körning via kod. Kodfragmentet nedan ger ett exempel på hur du utför den här uppgiften.

För att påskynda utvecklingen är följande hjälpmetoder användbara för att förenkla konfigurationen.

Interactable.GetDefaultInteractableStates() – skapar ett nytt tillstånd ScriptableObject med de fyra standardtillståndsvärdena som används i komponenten Interactable .

ThemeDefinition.GetDefaultThemeDefinition<T>() – Varje temamotor definierar en standardkonfiguration med rätt egenskaper som behövs för den temakörningstypen. Den här hjälpen skapar en definition för den angivna temamotortypen.

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

Temamotorer

En temamotor är en klass som sträcker sig från InteractableThemeBase klassen. De här klasserna instansieras vid körning och konfigureras med ett ThemeDefinition objekt enligt beskrivningen tidigare.

Standardtemamotorer

MRTK levereras med en standarduppsättning temamotorer som anges nedan:

Standardtemamotorerna finns under MRTK/SDK/Features/UX/Scripts/VisualThemes/ThemeEngines.

Anpassade temamotorer

Som sagt definieras en temamotor som en klass som sträcker sig från InteractableThemeBase klassen. Den nya temamotorn behöver därför bara utöka den här klassen och implementera följande:

Obligatoriska implementeringar

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

För den angivna egenskapen, som kan identifieras av ThemeStateProperty.Name, anger du dess aktuella tillståndsvärde på den riktade GameObject-värden (d.v.s. ange materialfärg osv.). Indexet anger det aktuella tillståndsvärdet för åtkomst och procentandelen, en flyttal mellan 0 och 1, används för att lätta/lerpa mellan värden.

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

För den angivna egenskapen, som kan identifieras av ThemeStateProperty.Name, returnerar du det aktuella värdet som angetts för målvärden GameObject (d.v.s. den aktuella materialfärgen, den aktuella lokala positionsförskjutningen osv.). Detta används främst för att cachelagra startvärdet när du lättar mellan tillstånden.

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

Returnerar ett ThemeDefinition objekt som definierar standardegenskaperna och konfigurationen som krävs för det anpassade temat

protected abstract void SetValue(ThemeStateProperty property, ThemePropertyValue value)

Skyddad variant av den offentliga SetValue() definitionen, förutom tillhandahållen ThemePropertyValue som ska anges i stället för att dirigera till att använda index- och/eller procentkonfiguration.

InteractableThemeBase.Init(GameObject host, ThemeDefinition settings)

Utför alla initieringssteg här som riktar sig till den angivna GameObject-parametern och använder de egenskaper och konfigurationer som definieras i parametern ThemeDefinition . Vi rekommenderar att du anropar base.Init(host, settings) i början av en åsidosättning.

InteractableThemeBase.IsEasingSupported

Om den anpassade temamotorn kan underlätta mellan värden som konfigureras via egenskapen ThemeDefinition.Easing .

InteractableThemeBase.AreShadersSupported

Om den anpassade temamotorn har stöd för skuggningsegenskaper. Vi rekommenderar att du utökar från InteractableShaderTheme för att dra nytta av den befintliga infrastrukturen för att effektivt ställa in/hämta skuggningsegenskaper via MaterialPropertyBlocks. Skuggningsegenskapsinformationen lagras i varje ThemeStateProperty via ThemeStateProperty.TargetShader och ThemeStateProperty.ShaderPropertyName.

Anteckning

Om du utökar InteractableShaderThemekan det också vara användbart att åsidosätta InteractableShaderTheme.DefaultShaderProperty via ny.

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

Dessutom utökar InteractableShaderTheme följande klasser nedan klassen som återigen använder MaterialPropertyBlocks för att ändra egenskapsvärden för skuggning. Den här metoden hjälper till med prestanda eftersom MaterialPropertyBlocks inte skapar nya instanser när värdena ändras. Åtkomsten till de typiska egenskaperna för materialklassen returnerar dock inte förväntade värden. Använd MaterialPropertyBlocks för att hämta och verifiera aktuella materialegenskapsvärden (t.ex . _Color eller _MainTex).

InteractableThemeBase.Reset

Instruerar temat att återställa ändrade egenskaper till sina ursprungliga värden som angavs på värden GameObject när den här temamotorn initierades.

Exempel på anpassad temamotor

Klassen nedan är ett exempel på en anpassad ny temamotor. Den här implementeringen hittar en MeshRenderer-komponent på det initierade värdobjektet och kontrollerar dess synlighet baserat på det aktuella tillståndet.

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

Exempel på slutpunkt till slutpunkt

Kodexemplet nedan utökar den anpassade temamotorn som definierades i det tidigare avsnittet och visar hur du styr det här temat vid körning. I synnerhet hur du ställer in aktuellt tillstånd i temat så att MeshRenderer-synligheten uppdateras på rätt sätt.

Anteckning

theme.OnUpdate(state,force) bör vanligtvis anropas i metoden Update() för att stödja temamotorer som använder lättnader/lerping mellan värden.

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

Se även