Vizuális témák – MRTK2

A témák lehetővé teszik az UX-eszközök rugalmas ellenőrzését a különböző állapotváltásokra reagálva. Ez magában foglalhatja egy gomb színének módosítását, a fókuszra adott elem átméretezését stb. A Visual Themes keretrendszer két fő részből áll: 1) konfigurációból és 2) futtatókörnyezeti motorból.

A témakonfigurációk tulajdonságok és típusok definíciói, míg a témamotorok olyan osztályok, amelyek a konfigurációkat felhasználják, és implementálják a logikát az átalakítások, anyagok és egyebek futásidőben történő frissítéséhez.

Témakonfiguráció

A témakonfigurációk olyan ScriptableObjects parancsprogramok, amelyek meghatározzák , hogyan inicializálják a témamotorokat futásidőben. Meghatározzák, hogy milyen tulajdonságokat és értékeket használjon a bemeneti vagy egyéb állapotváltozásokra válaszul az alkalmazás futtatásakor. Ahogy a ScriptableObjects objektumokat használja, a témakonfigurációk egyszer definiálhatók, majd újra felhasználhatók a különböző UX-összetevők között.

Új Theme objektum létrehozása:

  1. Kattintson a jobb gombbal a Projekt ablakban
  2. Válassza a Létrehozás>Mixed Reality Eszközkészlet-téma> lehetőséget

Példa témakonfigurációs objektumok a következő helyen MRTK/SDK/Features/UX/Interactable/Themestalálhatók: .

Theme ScriptableObject example in inspector

Állapotok

Új Themelétrehozásakor az első beállítás az, hogy mely állapotok érhetők el. Az States tulajdonság azt jelzi, hogy egy témakonfigurációnak hány értéket kell meghatároznia, mivel az állapotonként egy érték lesz. A fenti példaképben az Interakciós összetevőhöz definiált alapértelmezett állapotok azAlapértelmezett, a Fókusz, a Lenyomva és a Letiltva. Ezeket az DefaultInteractableStates (Assets/MRTK/SDK/Features/UX/Interactable/States) objektumfájl határozza meg.

Új State objektum létrehozása:

  1. Kattintson a jobb gombbal a Projekt ablakban
  2. Válassza a Létrehozás>Mixed Reality Eszközkészlet>állapota lehetőséget

States ScriptableObject example in inspector

A State ScriptableObject az állapotok listáját és az ezen állapotokhoz létrehozandó StateModel típust is meghatározza. A StateModel egy osztály, amely kibővíti BaseStateModel és implementálja az állapotgép-logikát az aktuális állapot futásidőben történő létrehozásához. Az osztály aktuális állapotát a témamotorok általában futásidőben használják annak diktálására, hogy milyen értékeket állítsanak be az anyagtulajdonságokhoz, a GameObject-átalakításokhoz és egyebekhez.

Témamotor tulajdonságai

Az államokon kívül az Theme eszköz meghatározza a témamotorok listáját és az ezekhez tartozó tulajdonságokat is. A témamotor ismét definiálja azt a logikát, amely futásidőben a megfelelő értékeket állítja be a GameObjecthez.

Az Theme objektumok több témamotort is definiálhatnak, hogy kifinomult vizualizációs állapotváltásokat érjenek el, amelyek több GameObject-tulajdonságot céloznak meg.

Téma futtatókörnyezete

Meghatározza a létrehozandó témamotor osztálytípusát

Enyhítése

Egyes témamotorok, ha igazként határozzák meg az IsEasingSupported tulajdonságukat, támogatják az állapotok közötti enyhítést. Ha például egy állapotváltozás történik, két szín között kell pöfögni. Az Időtartam másodpercben határozza meg, hogy mennyi ideig kell a kezdőértéktől a végpontig enyhíteni, és az Animációs görbe határozza meg az adott időszakban a változás sebességét.

Árnyékoló tulajdonságai

Egyes témamotorok, ha igazként határozzák meg az AreShadersSupported tulajdonságukat, futásidőben módosítják az egyes árnyékolótulajdonságokat. A Shader és a Property mezők határozzák meg a megcélzott shader tulajdonságot.

Témakonfiguráció létrehozása kóddal

Általában egyszerűbb a Témakonfigurációk tervezése a Unity felügyelőn keresztül, de vannak olyan esetek, amikor a témákat futásidőben, kóddal kell dinamikusan létrehozni. Az alábbi kódrészlet egy példát ad a feladat végrehajtására.

A fejlesztés felgyorsítása érdekében az alábbi segítő módszerek hasznosak a beállítás egyszerűsítéséhez.

Interactable.GetDefaultInteractableStates() – létrehoz egy új State ScriptableObject objektumot az Interakciós összetevőben használt négy alapértelmezett állapotértékkel.

ThemeDefinition.GetDefaultThemeDefinition<T>() – Minden témamotor egy alapértelmezett konfigurációt határoz meg az adott téma futtatókörnyezettípusához szükséges megfelelő tulajdonságokkal. Ez a segéd létrehoz egy definíciót a témamotor adott típusához.

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

Témamotorok

A témamotor az osztályból InteractableThemeBase kiterjeszthető osztály. Ezek az osztályok futtatókörnyezetben vannak példányosítva, és a ThemeDefinition korábban vázolt objektummal vannak konfigurálva.

Alapértelmezett témamotorok

Az MRTK az alábbiakban felsorolt alapértelmezett témamotor-készlettel rendelkezik:

Az alapértelmezett témamotorok a következő helyen MRTK/SDK/Features/UX/Scripts/VisualThemes/ThemeEnginestalálhatók: .

Egyéni témamotorok

Ahogy azt már említettem, a témamotor olyan osztályként van definiálva, amely kiterjeszti az InteractableThemeBase osztályt. Így az új témamotornak csak ezt az osztályt kell kiterjesztenie, és implementálnia kell a következőket:

Kötelező implementációk

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

Az adott tulajdonság esetében, amely a segítségével ThemeStateProperty.Nameazonosítható, állítsa be aktuális állapotértékét a megcélzott GameObject gazdagépen (azaz az anyag színének beállítása stb.). Az index az aktuális elérendő állapotértéket jelzi, és a százalékos érték (0 és 1 közötti lebegőpontos) az értékek közötti enyhítéshez/tűréshez használatos.

public abstract ThemePropertyValue GetProperty(ThemeStateProperty property)

Az adott tulajdonság esetében, amely a segítségével ThemeStateProperty.Nameazonosítható, adja vissza a megcélzott Gazdagép GameObject elemén beállított aktuális értéket (azaz az aktuális anyagszínt, az aktuális helyi pozíció eltolását stb.). Ezt elsősorban a kezdőérték gyorsítótárazására használják az állapotok közötti enyhítéskor.

public abstract ThemeDefinition GetDefaultThemeDefinition()

Egy ThemeDefinition objektumot ad vissza, amely meghatározza az egyéni témához szükséges alapértelmezett tulajdonságokat és konfigurációt

protected abstract void SetValue(ThemeStateProperty property, ThemePropertyValue value)

A nyilvános SetValue() definíció védett változata, kivéve a megadott ThemePropertyValue értéket, amelyet az index és/vagy százalékos konfiguráció használatára való átirányítás helyett kell beállítani.

InteractableThemeBase.Init(GameObject host, ThemeDefinition settings)

Itt hajtsa végre a megadott GameObject paramétert célzó inicializálási lépéseket, és használja a ThemeDefinition paraméterben meghatározott tulajdonságokat és konfigurációkat. Javasoljuk, hogy a felülbírálás elején hívja base.Init(host, settings) meg a parancsot.

InteractableThemeBase.IsEasingSupported

Ha az egyéni témamotor támogatja a tulajdonságon keresztül ThemeDefinition.Easing konfigurált értékek közötti enyhítést.

InteractableThemeBase.AreShadersSupported

Ha az egyéni témamotor támogatja az árnyékoló tulajdonságainak célzását. Javasoljuk, hogy InteractableShaderTheme a MaterialPropertyBlocks segítségével hatékonyan állítsa be/szerezze be a shader tulajdonságokat a meglévő infrastruktúra előnyeinek kihasználására. A shader tulajdonság információi a és ThemeStateProperty.ShaderPropertyNamea segítségével ThemeStateProperty.TargetShader tárolódnak.ThemeStateProperty

Megjegyzés

A kiterjesztés esetén InteractableShaderThemehasznos lehet az InteractableShaderTheme.DefaultShaderProperty felülbírálása is új módon.

Példakód: protected new const string DefaultShaderProperty = "_Color";

Továbbá az alábbi osztályok kiterjesztik az InteractableShaderTheme osztályt, amely ismét a MaterialPropertyBlocks használatával módosítja a shader tulajdonságértékeket. Ez a megközelítés segít a teljesítményben , mert a MaterialPropertyBlocks nem hoz létre új példányos anyagokat az értékek változásakor. A tipikus Material osztálytulajdonságokhoz való hozzáférés azonban nem a várt értékeket adja vissza. A MaterialPropertyBlocks használatával lekérheti és érvényesítheti az aktuális anyagtulajdonság-értékeket (például _Color vagy _MainTex).

InteractableThemeBase.Reset

Átirányítja a témát, hogy visszaállítsa a módosított tulajdonságokat az eredeti értékükre, amelyeket a témamotor inicializálásakor a GameObject gazdagépen beállítottak.

Példa egyéni témamotorra

Az alábbi osztály egy példa egy egyéni új témamotorra. Ez az implementáció egy MeshRenderer-összetevőt talál az inicializált gazdagépobjektumon, és az aktuális állapot alapján szabályozza annak láthatóságát.

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

Teljes példa

A korábbi szakaszban definiált egyéni témamotor kiterjesztve az alábbi kódpéldából megtudhatja, hogyan vezérelheti ezt a témát futásidőben. Különösen azt, hogyan állíthatja be az aktuális állapotot a témán, hogy a MeshRenderer láthatósága megfelelően frissüljön.

Megjegyzés

theme.OnUpdate(state,force) általában az Update() metódusban kell meghívni, hogy támogassa az értékek közötti enyhítést/lerpinget használó témamotorokat.

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

Lásd még