主题允许灵活地控制UX,以响应各种状态转换。这可能包括更改按钮的颜色,响应焦点而调整元素的大小等。VisualThemes框架由两个关键部分组成:
1)配置
2)运行时引擎
主题配置是属性和类型的定义,而主题引擎是消耗配置并实现在运行时更新转换,材质等的逻辑的类。
主题配置
主题配置是 ScriptableObjects,它们定义在运行时如何初始化主题引擎。它们定义了应用程序运行时响应输入或其他状态更改而使用的属性和值。作为 ScriptableObjects 资产,主题配置可以定义一次,然后在不同的UX组件之间重复使用。
要创建新Theme
资产:
- 在项目窗口中右键单击
- 选择Create > Mixed Reality Toolkit > Theme
示例主题配置资产可以在下找到 MRTK/SDK/Features/UX/Interactable/Themes
。
状态
创建新的时Theme
,首先要设置的是可用的状态。该指示主题的配置多少个值需要定义一样会有每个状态的一个值。在上面的示例图像中,为Interactable组件定义的默认状态为Default,Focus,Pressed和Disabled。这些在DefaultInteractableStates
(Assets/MRTK/SDK/Features/UX/Interactable/States)资产文件中定义。
要创建新State
资产:
- 在项目窗口中右键单击
- 选择 Create > Mixed Reality Toolkit > State
一个State
个ScriptableObject定义状态的两个列表以及类型StateModel创建这些状态。甲StateModel是延伸的类BaseStateModel
并实现状态机逻辑以在运行时生成的当前状态。主题引擎通常在运行时使用此类的当前状态来指示针对材质属性,GameObject转换等设置哪些值。
主题引擎属性
在州以外,Theme
资产还定义了主题引擎列表以及这些引擎的关联属性。甲主题引擎再次定义在运行时设置针对游戏物体正确的值的逻辑。
甲Theme
资产可以定义多个主题引擎来实现复杂的视觉状态转变定位多个游戏物体的性质。
运行时主题
定义将要创建的主题引擎的类类型
插值方式
一些主题引擎,如果将其属性IsEasingSupported定义为true,则支持状态之间的插值。例如,当状态发生变化时,在两种颜色之间切换。该时间以秒定义多久才能从开始值回落至终值和动画曲线定义在这段时间内的变化率。
着色器属性
如果某些主题引擎将其属性AreShadersSupported定义为true,则会在运行时修改特定的着色器属性。该着色器和属性字段定义着色器属性的目标。
通过代码创建主题配置
通常,通过Unity检查器设计主题配置更为容易,但是在某些情况下,必须在运行时通过代码动态生成主题。下面的代码段给出了如何完成此任务的示例。
为了帮助加快开发速度,以下辅助方法可用于简化设置。
Interactable.GetDefaultInteractableStates()
-使用在Interactable组件中使用的四个默认状态值创建一个新的States 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附带了一组默认的主题引擎,如下所示:
InteractableActivateTheme
InteractableAnimatorTheme
InteractableAudioTheme
InteractableColorChildrenTheme
InteractableColorTheme
InteractableGrabScaleTheme
InteractableMaterialTheme
InteractableOffsetTheme
InteractableRotationTheme
InteractableScaleTheme
InteractableShaderTheme
InteractableStringTheme
InteractableTextureTheme
ScaleOffsetColorTheme
默认主题引擎可以在下找到MRTK/SDK/Features/UX/Scripts/VisualThemes/ThemeEngines
。
自定义主题引擎
如前所述,主题引擎定义为从该类扩展的InteractableThemeBase
类。因此,新的主题引擎仅需要扩展此类并实现以下内容:
强制实施
public abstract void SetValue(ThemeStateProperty property, int index, float percentage)
(外部参考:Microsoft.MixedReality.Toolkit.UI.InteractableThemeBase.SetValue)
对于可以通过标识的给定属性,ThemeStateProperty.Name
在目标GameObject主机上设置其当前状态值(即,设置材质颜色等)。该指数表示当前状态值,以存取和百分比,0和1之间的浮动,用于缓解/值之间lerping。
public abstract ThemePropertyValue GetProperty(ThemeStateProperty property)
(外部参考:Microsoft.MixedReality.Toolkit.UI.InteractableThemeBase.GetProperty)
对于可以通过标识的给定属性,ThemeStateProperty.Name
返回在目标主机GameObject上设置的当前值(即,当前材质颜色,当前局部位置偏移量等)。这主要用于在状态之间放松时缓存起始值。
public abstract ThemeDefinition GetDefaultThemeDefinition()
(外部参考:Microsoft.MixedReality.Toolkit.UI.InteractableThemeBase.GetDefaultThemeDefinition)
返回一个ThemeDefinition
对象,该对象定义了自定义主题所需的默认属性和配置
protected abstract void SetValue(ThemeStateProperty property, ThemePropertyValue value)
受保护的公共SetValue()
定义变体,但提供了要设置的ThemePropertyValue而不是指示使用索引和/或百分比配置。
推荐的替代
InteractableThemeBase.Init(GameObject host, ThemeDefinition settings)
在此处针对提供的GameObject参数并使用ThemeDefinition参数中定义的属性和配置执行所有初始化步骤。建议base.Init(host, settings)
在覆盖的开头调用。
InteractableThemeBase.IsEasingSupported
如果自定义主题引擎可以支持通过ThemeDefinition.Easing
属性配置的值之间的缓动。
InteractableThemeBase.AreShadersSupported
自定义主题引擎是否可以支持定向着色器属性。建议InteractableShaderTheme
从现有基础结构中受益,扩展到通过MaterialPropertyBlocks有效设置/获取着色器属性。着色器属性信息存储在每个ThemeStateProperty
viaThemeStateProperty.TargetShader
和中ThemeStateProperty.ShaderPropertyName
。
[!NOTE]如果扩展InteractableShaderTheme
,则可以通过new覆盖InteractableShaderTheme.DefaultShaderProperty 。
示例代码: protected new const string DefaultShaderProperty = "_Color";
此外,下面的以下类扩展了InteractableShaderTheme
该类,该类再次使用MaterialPropertyBlocks修改着色器属性值。这种方法有助于提高性能,因为值更改时MaterialPropertyBlocks不会创建新的实例化材质。但是,访问典型的Material类属性将不会返回期望值。使用MaterialPropertyBlocks获取并验证当前的材料属性值(即_Color或_MainTex)。
InteractableColorChildrenTheme
InteractableColorTheme
InteractableTextureTheme
ScaleOffsetColorTheme
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()方法中调用[!NOTE], 以支持利用值之间的缓动/约束的主题引擎。
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;
}
}
也可以看看
原创文章,作者:游戏开发极客,如若转载,请注明出处:https://hololens2.cn/hololens2%e5%bc%80%e5%8f%91-mrtk2-%e8%a7%86%e8%a7%89%e4%b8%bb%e9%a2%98%ef%bc%88%e7%bc%96%e8%be%91%e4%b8%ad%ef%bc%89/