Temas visualesVisual themes

Los temas permiten un control flexible de los recursos de la experiencia de usuario en respuesta a diversas transiciones de estados.Themes allow for flexible control of UX assets in response to various states transitions. Esto puede implicar cambiar el color de un botón, cambiar el tamaño de un elemento en respuesta al foco, etc. El marco de visual themes se integra en dos partes clave: 1) configuración y 2) motores en tiempo de ejecución.This may involve changing a button's color, resizing an element in response to focus, etc. The Visual Themes framework is made up of two key pieces: 1) configuration and 2) runtime engines.

Las configuraciones de tema son definiciones de propiedades y tipos, mientras que los motores de temas son clases que consumen las configuraciones e implementan la lógica para actualizar transformaciones, materiales y mucho más en tiempo de ejecución.Theme configurations are definitions of properties and types while Theme Engines are classes that consume the configurations and implement the logic to update transforms, materials, and more at runtime.

Configuración del temaTheme configuration

Las configuraciones de tema son ScriptableObjects que definen cómo se inicializarán los motores de temas en tiempo de ejecución.Theme configurations are ScriptableObjects that define how Theme Engines will be initialized at runtime. Definen qué propiedades y valores usar en respuesta a los cambios de entrada u otros cambios de estado cuando se ejecuta la aplicación.They define what properties and values to utilize in response to input or other state changes when the app is running. Como recursos de ScriptableObjects, las configuraciones de tema se pueden definir una vez y, a continuación, volver a usarse en distintos componentes de la experiencia de usuario.As ScriptableObjects assets, theme configurations can be defined once and then re-used across different UX components.

Para crear un nuevo Theme recurso:To create a new Theme asset:

  1. Haga clic con el botón derecho en Project ventana.Right click in the Project Window
  2. Seleccione Create Mixed Reality Toolkit Theme > (Crear Mixed Reality Toolkit > tema).Select Create > Mixed Reality Toolkit > Theme

Los recursos de configuración de tema de ejemplo se pueden encontrar en MRTK/SDK/Features/UX/Interactable/Themes .Example Theme configuration assets can be found under MRTK/SDK/Features/UX/Interactable/Themes.

Ejemplo de ScriptableObject de tema en inspector

StatesStates

Al crear un nuevo Theme , lo primero que hay que establecer es qué estados están disponibles.When creating a new Theme, the first thing to set is what states are available. La propiedad States indica cuántos valores debe definir una configuración de tema, ya que habrá un valor por estado.The States property indicates how many values a Theme configuration needs to define as there will be one value per state. En la imagen de ejemplo anterior, los estados predeterminados definidos para el componente Interactable son Default, Focus, Pressed y Disabled.In the example image above, the default states defined for the Interactable component are Default, Focus, Pressed, and Disabled. Se definen en el archivo de recursos DefaultInteractableStates (Assets/MRTK/SDK/Features/UX/Interactable/States).These are defined in the DefaultInteractableStates (Assets/MRTK/SDK/Features/UX/Interactable/States) asset file.

Para crear un nuevo State recurso:To create a new State asset:

  1. Haga clic con el botón derecho en Project ventana.Right click in the Project Window
  2. Seleccione Create Mixed Reality Toolkit State > (Crear Mixed Reality Toolkit > estado).Select Create > Mixed Reality Toolkit > State

Ejemplo de ScriptableObject de estados en inspector

ScriptableObject define tanto la lista de estados como el State tipo de StateModel que se creará para estos estados.A State ScriptableObject defines both the list of states as well as the type of StateModel to create for these states. StateModel es una clase que extiende e implementa la lógica de la máquina de estado para generar el estado actual en tiempo de BaseStateModel ejecución.A StateModel is a class that extends BaseStateModel and implements the state machine logic to generate the current state at runtime. Los motores de temas en tiempo de ejecución suelen usar el estado actual de esta clase para determinar qué valores se establecerán en las propiedades de material, las transformaciones de GameObject y mucho más.The current state from this class is generally used by Theme Engines at runtime to dictate what values to set against material properties, GameObject transforms, and more.

Propiedades del motor de temasTheme engine properties

Fuera de Estados, un recurso también define una lista de motores de Theme temas y las propiedades asociadas para estos motores.Outside of States, a Theme asset also defines a list of Theme Engines and the associated properties for these engines. Un motor de tema define de nuevo la lógica para establecer los valores correctos en un GameObject en tiempo de ejecución.A Theme engine again defines the logic to set the correct values against a GameObject at runtime.

Un recurso puede definir varios motores de temas para lograr transiciones sofisticadas de estados visuales Theme destinadas a varias propiedades gameObject.A Theme asset can define multiple Theme Engines to achieve sophisticated visual states transitions targeting multiple GameObject properties.

Tiempo de ejecución de temasTheme Runtime

Define el tipo de clase del motor de tema que se creará.Defines the class type of the Theme engine that will be created

FacilitarEasing

Algunos motores de temas, si definen su propiedad IsEasingSupported como true, admiten la aceleración entre estados.Some Theme Engines, if they define their property IsEasingSupported as true, support easing between states. Por ejemplo, la lerping entre dos colores cuando se produce un cambio de estado.For example, lerping between two colors when a state change occurs. La duración define en segundos cuánto tiempo se va a facilitar desde el valor inicial hasta el valor final y la curva de animación define la tasa de cambio durante ese período de tiempo.The Duration defines in seconds how long to ease from start value to end value and the Animation Curve defines the rate of change during that time period.

Propiedades del sombreadorShader properties

Algunos motores de temas , si definen su propiedad AreShadersSupported como true, modificarán propiedades de sombreador concretas en tiempo de ejecución.Some Theme Engines, if they define their property AreShadersSupported as true, will modify particular shader properties at runtime. Los campos Sombreador y Propiedad definen la propiedad del sombreador de destino.The Shader and Property fields define the shader property to target.

Creación de una configuración de tema mediante códigoCreate a theme configuration via code

En general, es más fácil diseñar configuraciones de tema a través del inspector de Unity, pero hay casos en los que los temas se deben generar dinámicamente en tiempo de ejecución a través del código.In general, it is easier to design Theme configurations via the Unity inspector but there are cases where Themes must be dynamically generated at runtime via code. El fragmento de código siguiente proporciona un ejemplo de cómo realizar esta tarea.The code snippet below gives an example of how to accomplish this task.

Para ayudar a acelerar el desarrollo, los siguientes métodos auxiliares son útiles para simplificar la configuración.To help expedite development, the following helper methods are useful for simplifying setup.

Interactable.GetDefaultInteractableStates(): crea un nuevo objeto States ScriptableObject con los cuatro valores de estado predeterminados utilizados en el componente Interactable.Interactable.GetDefaultInteractableStates() - creates a new States ScriptableObject with the four default state values used in the Interactable component.

ThemeDefinition.GetDefaultThemeDefinition<T>() - Cada motor de temas define una configuración predeterminada con las propiedades correctas necesarias para ese tipo de tiempo de ejecución de tema.ThemeDefinition.GetDefaultThemeDefinition<T>() - Every Theme Engine defines a default configuration with the correct properties needed for that Theme runtime type. Este asistente crea una definición para el tipo de motor de tema especificado.This helper creates a definition for the given Theme Engine type.

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

Motores de temasTheme engines

Un motor de temas es una clase que se extiende desde la clase InteractableThemeBase .A Theme Engine is a class that extends from the InteractableThemeBase class. Se crea una instancia de estas clases en tiempo de ejecución y se configuran con ThemeDefinition un objeto como se ha descrito anteriormente.These classes are instantiated at runtime and configured with a ThemeDefinition object as outlined earlier.

Motores de temas predeterminadosDefault theme engines

MRTK se distribuye con un conjunto predeterminado de motores de temas que se enumeran a continuación:MRTK ships with a default set of Theme Engines listed below:

Los motores de temas predeterminados se pueden encontrar en MRTK/SDK/Features/UX/Scripts/VisualThemes/ThemeEngines .The default Theme Engines can be found under MRTK/SDK/Features/UX/Scripts/VisualThemes/ThemeEngines.

Motores de temas personalizadosCustom theme engines

Como se ha indicado, un motor de temas se define como una clase que se extiende desde la InteractableThemeBase clase .As stated, a Theme Engine is defined as a class that extends from the InteractableThemeBase class. Por lo tanto, el nuevo motor de temas solo necesita extender esta clase e implementar lo siguiente:Thus, new Theme Engine need only extend this class and implement the following:

Implementaciones obligatoriasMandatory implementations

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

Para la propiedad determinada, que se puede identificar mediante , establezca su valor de estado actual en el host gameObject de destino ThemeStateProperty.Name (es decir,For the given property, which can be identified by ThemeStateProperty.Name, set its current state value on the targeted GameObject host (i.e establecer el color del material, etc.).set the material color, etc). El índice indica el valor de estado actual al que se va a obtener acceso y el porcentaje , un valor float entre 0 y 1, se usa para la aceleración o el equilibrio entre valores.The index indicates the current state value to access and the percentage, a float between 0 and 1, is used for easing/lerping between values.

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

Para la propiedad determinada, que se puede identificar mediante , devuelve el valor actual establecido en el objeto GameObject host ThemeStateProperty.Name de destino (es decir,For the given property, which can be identified by ThemeStateProperty.Name, return the current value set on the targeted Host GameObject (i.e el color del material actual, el desplazamiento de la posición local actual, etc.).the current material color, the current local position offset, etc). Esto se usa principalmente para almacenar en caché el valor de inicio cuando se acelera entre estados.This is primarily used for caching the start value when easing between states.

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

Devuelve un ThemeDefinition objeto que define las propiedades y la configuración predeterminadas necesarias para el tema personalizado.Returns a ThemeDefinition object that defines the default properties and configuration needed for the custom theme

protected abstract void SetValue(ThemeStateProperty property, ThemePropertyValue value)

Variante protegida de la definición pública, excepto el valor ThemePropertyValue proporcionado para establecer en lugar de dirigir para usar la SetValue() configuración de índice o porcentaje.Protected variant of the public SetValue() definition, except provided ThemePropertyValue to set instead of directing to use index and/or percentage configuration.

InteractableThemeBase.Init(GameObject host, ThemeDefinition settings)

Realice los pasos de inicialización aquí dirigidos al parámetro GameObject proporcionado y use las propiedades y configuraciones definidas en el parámetro ThemeDefinition.Perform any initialization steps here targeting the provided GameObject parameter and using the properties and configurations defined in the ThemeDefinition parameter. Se recomienda llamar a base.Init(host, settings) al principio de una invalidación.It is recommended to call base.Init(host, settings) at the beginning of an override.

InteractableThemeBase.IsEasingSupported

Si el motor de temas personalizado puede admitir la aceleración entre los valores que se configuran a través de la ThemeDefinition.Easing propiedad .If the custom Theme Engine can support easing between values which is configured via the ThemeDefinition.Easing property.

InteractableThemeBase.AreShadersSupported

Si el motor de temas personalizado puede admitir propiedades de sombreador de destino.If the custom Theme Engine can support targeting shader properties. Se recomienda ampliar desde para beneficiarse de la infraestructura existente para establecer o obtener eficazmente las propiedades del InteractableShaderTheme sombreador a través de MaterialPropertyBlocks.It is recommended to extend from InteractableShaderTheme to benefit from the existing infrastructure to efficiently set/get shader properties via MaterialPropertyBlocks. La información de la propiedad del sombreador se almacena en cada ThemeStateProperty a través de y ThemeStateProperty.TargetShader ThemeStateProperty.ShaderPropertyName .The shader property information is stored in each ThemeStateProperty via ThemeStateProperty.TargetShader and ThemeStateProperty.ShaderPropertyName.

Nota

Si extiende InteractableShaderTheme , también puede ser útil invalidar InteractableShaderTheme.DefaultShaderProperty mediante el nuevo.If extending InteractableShaderTheme, it can also be useful to override the InteractableShaderTheme.DefaultShaderProperty via new.

Código de ejemplo: protected new const string DefaultShaderProperty = "_Color";Example code: protected new const string DefaultShaderProperty = "_Color";

Además, las siguientes clases amplían la clase que usa de nuevo InteractableShaderTheme MaterialPropertyBlocks para modificar los valores de propiedad del sombreador.Furthermore, the following classes below extend the InteractableShaderTheme class which again uses MaterialPropertyBlocks to modify shader property values. Este enfoque ayuda al rendimiento porque MaterialPropertyBlocks no crea nuevos materiales con instancias cuando cambian los valores.This approach helps performance because MaterialPropertyBlocks do not create new instanced materials when values change. Sin embargo, el acceso a las propiedades de clase Material típicas no devolverá los valores esperados.However, accessing the typical Material class properties will not return expected values. Use MaterialPropertyBlocks para obtener y validar los valores de propiedad de material actuales (es decir,Use MaterialPropertyBlocks to get and validate current material property values (i.e _Color o _MainTex)._Color or _MainTex).

InteractableThemeBase.Reset

Dirige al tema para restablecer las propiedades modificadas a sus valores originales establecidos en el gameObject del host cuando se inicializó este motor de tema.Directs the theme to reset any modified properties back to their original values that were set on the host GameObject when this theme engine was initialized.

Ejemplo de motor de tema personalizadoCustom theme engine example

La clase siguiente es un ejemplo de un nuevo motor de temas personalizado.The class below is an example of a custom new Theme Engine. Esta implementación buscará un componente MeshRenderer en el objeto host inicializado y controlará su visibilidad en función del estado actual.This implementation will find a MeshRenderer component on the initialized host object and control its visibility based on the current state.

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

Ejemplo de un extremo a otroEnd-to-end example

Al extenderse fuera del motor de temas personalizado definido en la sección anterior, el ejemplo de código siguiente muestra cómo controlar este tema en tiempo de ejecución.Extending off of the custom Theme Engine defined in the earlier section, the code example below demonstrates how to control this theme at runtime. En concreto, cómo establecer el estado actual en el tema para que la visibilidad de MeshRenderer se actualice correctamente.In particular, how to set the current state on the theme so the MeshRenderer visibility is updated appropriately.

Nota

theme.OnUpdate(state,force) Por lo general, se debe llamar a en el método Update() para admitir los motores de temas que usan la aceleración o el equilibrio entre valores.theme.OnUpdate(state,force) should generally be called in the Update() method to support Theme Engines that utilize easing/lerping between values.

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

Consulte tambiénSee also