Partager via


Thèmes visuels — MRTK2

Les thèmes permettent un contrôle flexible des ressources d’expérience utilisateur en réponse à diverses transitions d’états. Cela peut impliquer la modification de la couleur d’un bouton, le redimensionnement d’un élément en réponse au focus, etc. L’infrastructure Thèmes visuels se compose de deux éléments clés : 1) la configuration et 2) les moteurs d’exécution.

Les configurations de thème sont des définitions de propriétés et de types, tandis que les moteurs de thème sont des classes qui consomment les configurations et implémentent la logique pour mettre à jour les transformations, les matériaux et bien plus encore au moment de l’exécution.

Configuration du thème

Les configurations de thème sont des ScriptableObjects qui définissent la façon dont les moteurs de thème seront initialisés au moment de l’exécution. Ils définissent les propriétés et les valeurs à utiliser en réponse aux changements d’état d’entrée ou autres lors de l’exécution de l’application. En tant que ressources ScriptableObjects , les configurations de thème peuvent être définies une seule fois, puis réutilisées entre différents composants d’expérience utilisateur.

Pour créer une Theme ressource :

  1. Cliquez avec le bouton droit dans la fenêtre Projet
  2. Sélectionnez Créer>Mixed Reality Thème du kit de ressources>

Vous trouverez des exemples de ressources de configuration de thème sous MRTK/SDK/Features/UX/Interactable/Themes.

Exemple de script de thème dans l’inspecteur

États

Lors de la création d’un nouveau Theme, la première chose à définir est les états disponibles. La propriété States indique le nombre de valeurs qu’une configuration de thème doit définir, car il y aura une valeur par état. Dans l’exemple d’image ci-dessus, les états par défaut définis pour le composant Interactable sont Default, Focus, Pressed et Disabled. Elles sont définies dans le DefaultInteractableStates fichier de ressources (Assets/MRTK/SDK/Features/UX/Interactable/States).

Pour créer une State ressource :

  1. Cliquez avec le bouton droit dans la fenêtre Projet
  2. Sélectionnez Créer>Mixed Reality État du kit de ressources>

Exemple d’états ScriptableObject dans l’inspecteur

Un State ScriptableObject définit à la fois la liste des états ainsi que le type de StateModel à créer pour ces états. Un StateModel est une classe qui étend BaseStateModel et implémente la logique de l’ordinateur d’état pour générer l’état actuel au moment de l’exécution. L’état actuel de cette classe est généralement utilisé par les moteurs de thème au moment de l’exécution pour dicter les valeurs à définir par rapport aux propriétés matérielles, aux transformations GameObject, etc.

Propriétés du moteur de thème

En dehors des États, une Theme ressource définit également une liste de moteurs de thème et les propriétés associées pour ces moteurs. Un moteur de thème définit à nouveau la logique pour définir les valeurs correctes sur un GameObject au moment de l’exécution.

Une Theme ressource peut définir plusieurs moteurs de thème pour obtenir des transitions d’états visuels sophistiquées ciblant plusieurs propriétés GameObject.

Runtime de thème

Définit le type de classe du moteur de thème qui sera créé

Assouplissement

Certains moteurs de thème, s’ils définissent leur propriété IsEasingSupported comme true, prennent en charge l’accélération entre états. Par exemple, le lerping entre deux couleurs lorsqu’un changement d’état se produit. La durée définit en secondes la durée à laquelle faciliter la transition de la valeur de début à la valeur de fin, et la courbe d’animation définit le taux de modification au cours de cette période.

Propriétés du nuanceur

Certains moteurs de thème, s’ils définissent leur propriété AreShadersSupported comme true, modifient des propriétés de nuanceur particulières au moment de l’exécution. Les champs Nuanceur et Propriété définissent la propriété de nuanceur à cibler.

Créer une configuration de thème via du code

En général, il est plus facile de concevoir des configurations de thème via l’inspecteur Unity, mais il existe des cas où les thèmes doivent être générés dynamiquement au moment de l’exécution via du code. L’extrait de code ci-dessous fournit un exemple de la façon d’accomplir cette tâche.

Pour accélérer le développement, les méthodes d’assistance suivantes sont utiles pour simplifier la configuration.

Interactable.GetDefaultInteractableStates() - crée un objet States ScriptableObject avec les quatre valeurs d’état par défaut utilisées dans le composant Interactable .

ThemeDefinition.GetDefaultThemeDefinition<T>() - Chaque moteur de thème définit une configuration par défaut avec les propriétés appropriées nécessaires pour ce type d’exécution de thème. Cette assistance crée une définition pour le type de moteur de thème donné.

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

Moteurs de thème

Un moteur de thème est une classe qui s’étend à partir de la InteractableThemeBase classe . Ces classes sont instanciées au moment de l’exécution et configurées avec un ThemeDefinition objet comme décrit précédemment.

Moteurs de thème par défaut

MRTK est fourni avec un ensemble par défaut de moteurs de thème répertoriés ci-dessous :

Les moteurs de thème par défaut se trouvent sous MRTK/SDK/Features/UX/Scripts/VisualThemes/ThemeEngines.

Moteurs de thème personnalisés

Comme indiqué, un moteur de thème est défini comme une classe qui s’étend à partir de la InteractableThemeBase classe . Par conséquent, le nouveau moteur de thème n’a besoin que d’étendre cette classe et d’implémenter les éléments suivants :

Implémentations obligatoires

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

Pour la propriété donnée, qui peut être identifiée par ThemeStateProperty.Name, définissez sa valeur d’état actuel sur l’hôte GameObject ciblé (c’est-à-dire définir la couleur du matériau, etc.). L’index indique la valeur d’état actuelle à accéder et le pourcentage, un float compris entre 0 et 1, est utilisé pour l’accélération/lerping entre les valeurs.

public abstract ThemePropertyValue GetProperty(ThemeStateProperty property)

Pour la propriété donnée, qui peut être identifiée par ThemeStateProperty.Name, retournez la valeur actuelle définie sur le GameObject hôte ciblé (c’est-à-dire la couleur de matériau actuelle, le décalage de position local actuel, etc.). Il est principalement utilisé pour la mise en cache de la valeur de début lors de l’accélération entre les états.

public abstract ThemeDefinition GetDefaultThemeDefinition()

Retourne un ThemeDefinition objet qui définit les propriétés par défaut et la configuration nécessaires pour le thème personnalisé

protected abstract void SetValue(ThemeStateProperty property, ThemePropertyValue value)

Variante protégée de la définition publique SetValue() , à l’exception de ThemePropertyValue à définir au lieu d’utiliser la configuration d’index et/ou de pourcentage.

InteractableThemeBase.Init(GameObject host, ThemeDefinition settings)

Effectuez ici toutes les étapes d’initialisation en ciblant le paramètre GameObject fourni et en utilisant les propriétés et configurations définies dans le paramètre ThemeDefinition . Il est recommandé d’appeler base.Init(host, settings) au début d’une substitution.

InteractableThemeBase.IsEasingSupported

Si le moteur de thème personnalisé peut prendre en charge l’accélération entre les valeurs configurées via la ThemeDefinition.Easing propriété .

InteractableThemeBase.AreShadersSupported

Si le moteur de thème personnalisé peut prendre en charge le ciblage des propriétés du nuanceur. Il est recommandé d’étendre de InteractableShaderTheme pour tirer parti de l’infrastructure existante pour définir/obtenir efficacement des propriétés de nuanceur via MaterialPropertyBlocks. Les informations de propriété du nuanceur sont stockées dans chaque ThemeStateProperty via ThemeStateProperty.TargetShader et ThemeStateProperty.ShaderPropertyName.

Notes

Si vous étendez InteractableShaderTheme, il peut également être utile de remplacer InteractableShaderTheme.DefaultShaderProperty via new.

Exemple de code : protected new const string DefaultShaderProperty = "_Color";

En outre, les classes suivantes ci-dessous étendent la InteractableShaderTheme classe qui utilise à nouveau MaterialPropertyBlocks pour modifier les valeurs des propriétés du nuanceur. Cette approche améliore les performances , car MaterialPropertyBlocks ne crée pas de nouveaux matériaux instanceux lorsque les valeurs changent. Toutefois, l’accès aux propriétés de la classe Material standard ne retourne pas les valeurs attendues. Utilisez MaterialPropertyBlocks pour obtenir et valider les valeurs de propriété de matériau actuelles (c’est-à-dire _Color ou _MainTex).

InteractableThemeBase.Reset

Indique au thème de réinitialiser toutes les propriétés modifiées à leurs valeurs d’origine qui ont été définies sur le GameObject hôte lors de l’initialisation de ce moteur de thème.

Exemple de moteur de thème personnalisé

La classe ci-dessous est un exemple de nouveau moteur de thème personnalisé. Cette implémentation recherche un composant MeshRenderer sur l’objet hôte initialisé et contrôle sa visibilité en fonction de l’état actuel.

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

Exemple de bout en bout

S’étendant sur le moteur de thème personnalisé défini dans la section précédente, l’exemple de code ci-dessous montre comment contrôler ce thème au moment de l’exécution. En particulier, comment définir l’état actuel sur le thème afin que la visibilité de MeshRenderer soit mise à jour de manière appropriée.

Notes

theme.OnUpdate(state,force) doit généralement être appelé dans la méthode Update() pour prendre en charge les moteurs de thème qui utilisent l’accélération/lerping entre les valeurs.

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

Voir aussi