시각적 테마 - MRTK2

테마를 사용하면 다양한 상태 전환에 대응하여 UX 자산을 유연하게 제어할 수 있습니다. 여기에는 단추의 색 변경, 포커스에 대한 응답으로 요소 크기 조정 등이 포함될 수 있습니다. Visual Themes 프레임워크는 1) 구성과 2) 런타임 엔진의 두 가지 주요 요소로 구성됩니다.

테마 구성은 속성 및 형식의 정의이며 , 테마 엔진 은 구성을 사용하고 런타임에 변환, 재질 등을 업데이트하는 논리를 구현하는 클래스입니다.

테마 구성

테마 구성은 런타임에 테마 엔진을 초기화하는 방법을 정의하는 ScriptableObjects 입니다. 앱이 실행 중일 때 입력 또는 기타 상태 변경에 대한 응답으로 활용할 속성과 값을 정의합니다. ScriptableObjects 자산으로 테마 구성을 한 번 정의한 다음 다른 UX 구성 요소에서 다시 사용할 수 있습니다.

Theme 자산을 만들려면 다음을 수행합니다.

  1. Project 창을 마우스 오른쪽 단추로 클릭합니다.
  2. 만들기>Mixed Reality Toolkit> 선택

예제 테마 구성 자산은 아래에서 MRTK/SDK/Features/UX/Interactable/Themes찾을 수 있습니다.

Theme ScriptableObject example in inspector

상태

Theme상태를 만들 때 가장 먼저 설정하는 것은 사용 가능한 상태입니다. 상태 속성은 테마 구성에서 정의해야 하는 값 수를 나타냅니다. 상태당 값이 하나뿐입니다. 위의 예제 이미지에서 상호 작용 가능 구성 요소에 대해 정의된 기본 상태는Default, Focus, PressedDisabled입니다. 이는 (Assets/MRTK/SDK/Features/UX/Interactable/States) 자산 파일에 정의 DefaultInteractableStates 되어 있습니다.

State 자산을 만들려면 다음을 수행합니다.

  1. Project 창을 마우스 오른쪽 단추로 클릭합니다.
  2. 만들기>Mixed Reality Toolkit>State 선택

States ScriptableObject example in inspector

State ScriptableObject는 상태 목록과 이러한 상태에 대해 만들 StateModel 형식을 모두 정의합니다. StateModel은 런타임에 현재 상태를 생성하기 위해 상태 머신 논리를 확장하고 BaseStateModel 구현하는 클래스입니다. 이 클래스의 현재 상태는 일반적으로 런타임에 테마 엔진에서 재료 속성, GameObject 변환 등에 대해 설정할 값을 지정하는 데 사용됩니다.

테마 엔진 속성

상태 외부에서 자산은 Theme 테마 엔진 목록과 이러한 엔진에 대한 관련 속성도 정의합니다. 테마 엔진은 런타임에 GameObject에 대해 올바른 값을 설정하는 논리를 다시 정의합니다.

자산은 Theme 여러 GameObject 속성을 대상으로 하는 정교한 시각적 상태 전환을 달성하기 위해 여러 테마 엔진을 정의할 수 있습니다.

테마 런타임

만들 테마 엔진의 클래스 형식을 정의합니다.

완화

일부 테마 엔진은IsEasingSupported 속성을 true로 정의하는 경우 상태 간 완화를 지원합니다. 예를 들어 상태 변경이 발생할 때 두 색 사이를 붙입니다. Duration은 시작 값에서 끝 값까지의 기간을 초 단위로 정의하고 애니메이션 곡선은 해당 기간 동안 변경 속도를 정의합니다.

셰이더 속성

일부 테마 엔진은AreShadersSupported 속성을 true로 정의하는 경우 런타임에 특정 셰이더 속성을 수정합니다. 셰이더속성 필드는 대상으로 지정할 셰이더 속성을 정의합니다.

코드를 통해 테마 구성 만들기

일반적으로 Unity 검사기를 통해 테마 구성을 디자인하는 것이 더 쉽지만 코드를 통해 런타임에 테마를 동적으로 생성해야 하는 경우가 있습니다. 아래 코드 조각은 이 작업을 수행하는 방법에 대한 예제를 제공합니다.

신속한 개발을 돕기 위해 다음 도우미 메서드는 설정을 간소화하는 데 유용합니다.

Interactable.GetDefaultInteractableStates() - Interactable 구성 요소에 사용되는 네 개의 기본 상태 값을 사용하여 새 State ScriptableObject를 만듭니다.

ThemeDefinition.GetDefaultThemeDefinition<T>() - 모든 테마 엔진은 해당 테마 런타임 형식에 필요한 올바른 속성을 사용하여 기본 구성을 정의합니다. 이 도우미는 지정된 테마 엔진 형식에 대한 정의를 만듭니다.

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

테마 엔진

테마 엔진은 클래스에서 확장되는 클래스입니다InteractableThemeBase. 이러한 클래스는 런타임에 인스턴스화되고 앞에서 설명한 ThemeDefinition 대로 개체로 구성됩니다.

기본 테마 엔진

MRTK는 아래에 나열된 기본 테마 엔진 집합과 함께 제공됩니다.

기본 테마 엔진은 아래에서 MRTK/SDK/Features/UX/Scripts/VisualThemes/ThemeEngines찾을 수 있습니다.

사용자 지정 테마 엔진

설명한 대로 테마 엔진은 클래스에서 InteractableThemeBase 확장되는 클래스로 정의됩니다. 따라서 새 테마 엔진은 이 클래스만 확장하고 다음을 구현해야 합니다.

필수 구현

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

지정된 속성으로 식별 ThemeStateProperty.Name할 수 있는 경우 대상 GameObject 호스트(즉, 재질 색 설정 등)에서 현재 상태 값을 설정합니다. 인덱스는 액세스할 현재 상태 값을 나타내며, 0에서 1 사이의 부동 소수 요소는 값 간의 감속/lerping에 사용됩니다.

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

지정된 속성에 대해 ThemeStateProperty.Name식별할 수 있는 대상 Host GameObject에 설정된 현재 값(즉, 현재 재질 색, 현재 로컬 위치 오프셋 등)을 반환합니다. 이는 주로 상태 간에 완화할 때 시작 값을 캐싱하는 데 사용됩니다.

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

사용자 지정 테마에 ThemeDefinition 필요한 기본 속성 및 구성을 정의하는 개체를 반환합니다.

protected abstract void SetValue(ThemeStateProperty property, ThemePropertyValue value)

인덱스 및/또는 백분율 구성을 사용하도록 지시하는 대신 설정하도록 제공된 ThemePropertyValue를 제외하고 공용 SetValue() 정의의 보호된 변형입니다.

InteractableThemeBase.Init(GameObject host, ThemeDefinition settings)

제공된 GameObject 매개 변수를 대상으로 하고 ThemeDefinition 매개 변수에 정의된 속성 및 구성을 사용하여 여기에서 초기화 단계를 수행합니다. 재정의를 시작할 때 호출 base.Init(host, settings) 하는 것이 좋습니다.

InteractableThemeBase.IsEasingSupported

사용자 지정 테마 엔진이 속성을 통해 ThemeDefinition.Easing 구성된 값 간의 감속을 지원할 수 있는 경우

InteractableThemeBase.AreShadersSupported

사용자 지정 테마 엔진이 셰이더 속성의 대상 지정을 지원할 수 있는 경우 기존 인프라의 InteractableShaderTheme 이점을 활용하기 위해 MaterialPropertyBlocks를 통해 셰이더 속성을 효율적으로 설정/가져오는 것이 좋습니다. 셰이더 속성 정보는 각각 ThemeStateProperty 을 통해 ThemeStateProperty.TargetShader 저장됩니다 ThemeStateProperty.ShaderPropertyName.

참고

확장하는 경우 새로 만들기를 InteractableShaderTheme통해 InteractableShaderTheme.DefaultShaderProperty를 재정의하는 것도 유용할 수 있습니다.

예제 코드: protected new const string DefaultShaderProperty = "_Color";

또한 아래 클래스는 MaterialPropertyBlocks를 다시 사용하여 셰이더 속성 값을 수정하는 클래스를 확장 InteractableShaderTheme 합니다. 이 방법은 값이 변경될 때 MaterialPropertyBlocks가 새 인스턴스 재질을 만들지 않기 때문에 성능에 도움이 됩니다. 그러나 일반적인 Material 클래스 속성에 액세스해도 예상 값은 반환되지 않습니다. MaterialPropertyBlocks를 사용하여 현재 재질 속성 값(예: _Color 또는 _MainTex)을 가져와 유효성을 검사합니다.

InteractableThemeBase.Reset

이 테마 엔진이 초기화되었을 때 호스트 GameObject에 설정된 원래 값으로 수정된 속성을 다시 설정하도록 테마를 지시합니다.

사용자 지정 테마 엔진 예제

아래 클래스는 사용자 지정 새 테마 엔진의 예입니다. 이 구현은 초기화된 호스트 개체에서 MeshRenderer 구성 요소를 찾고 현재 상태에 따라 표시 유형을 제어합니다.

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

엔드투엔드 예제

이전 섹션에서 정의한 사용자 지정 테마 엔진을 확장하는 아래 코드 예제에서는 런타임에 이 테마를 제어하는 방법을 보여 줍니다. 특히 MeshRenderer 표시 유형이 적절하게 업데이트되도록 테마에서 현재 상태를 설정하는 방법입니다.

참고

theme.OnUpdate(state,force) 는 일반적으로 Update() 메서드에서 호출하여 값 간의 감속/lerping을 활용하는 테마 엔진을 지원해야 합니다.

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

참조