如何添加近距交互性 — MRTK2

近距交互采用触摸和抓取形式。 触摸和抓取事件分别由 PokePointerSpherePointer 作为指针事件引发。

要侦听特定 GameObject 上的触摸和/或抓取输入事件,需要执行三个关键步骤。

  1. 确保相关指针已在主 MRTK 配置文件中注册。
  2. 确保所需的 GameObject 具有适当的抓取触摸脚本组件和 Unity Collider
  3. 在所需 GameObject 的附加脚本上实现输入处理程序接口,以侦听抓取触摸事件。

添加抓取交互

  1. 确保在 MRTK 指针配置文件中注册了 SpherePointer

    默认 MRTK 配置文件和默认 HoloLens 2 配置文件已包含 SpherePointer。 可以通过选择 MRTK 配置文件并导航到“输入”>“指针”>“指针选项”来确认将创建 SpherePointer。 默认的 GrabPointer 预制件 (Assets/MRTK/SDK/Features/UX/Prefabs/Pointers) 应与关节手的控制器类型一起列出。 只要自定义预制件实现 SpherePointer 类,就可以使用该预制件。

    Grab Pointer Profile Example

    默认抓取指针会在抓取点周围的锥形区域内查询附近的对象,以匹配默认的 HoloLens 2 界面。

    Conical Grab Pointer

  2. 在应该可抓取的 GameObject 上,添加一个 NearInteractionGrabbable 以及一个碰撞体。

    确保 GameObject 的层位于可抓取的层上。 默认情况下,除“空间感知”和“忽略光线投射”之外的所有层都是可抓取的。 通过检查 GrabPointer 预制件中的抓取层蒙板来查看哪些层是可抓取的

  3. 在 GameObject 或其任一上级上,添加一个脚本组件,用于实现 IMixedRealityPointerHandler 接口。 具有 NearInteractionGrabbable 的对象的任一上级也将能够接收指针事件。

抓取代码示例

下面的脚本会输出事件是触摸还是抓取。 在相关的 IMixedRealityPointerHandler 接口函数中,可以通过 MixedRealityPointerEventData 查看触发该事件的指针类型。 如果指针是 SpherePointer,则交互为抓取

public class PrintPointerEvents : MonoBehaviour, IMixedRealityPointerHandler
{
    public void OnPointerDown(MixedRealityPointerEventData eventData)
    {
        if (eventData.Pointer is SpherePointer)
        {
            Debug.Log($"Grab start from {eventData.Pointer.PointerName}");
        }
        if (eventData.Pointer is PokePointer)
        {
            Debug.Log($"Touch start from {eventData.Pointer.PointerName}");
        }
    }

    public void OnPointerClicked(MixedRealityPointerEventData eventData) {}
    public void OnPointerDragged(MixedRealityPointerEventData eventData) {}
    public void OnPointerUp(MixedRealityPointerEventData eventData) {}
}

添加触摸交互

在 UnityUI 元素上添加触摸交互的过程与 vanilla 3D GameObject 不同。 可以跳到以下部分 (Unity UI),启用 Unity UI 组件

但是,对于这两种类型的 UX 元素,请确保在 MRTK 指针配置文件中注册了 PokePointer

默认 MRTK 配置文件和默认 HoloLens 2 配置文件已包含 PokePointer。 可以通过选择 MRTK 配置文件并导航到“输入”>“指针”>“指针选项”来确认将创建 PokePointer。 默认的 PokePointer 预制件 (Assets/MRTK/SDK/Features/UX/Prefabs/Pointers) 应与关节手的控制器类型一起列出。 只要自定义预制件实现 PokePointer 类,就可以使用该预制件。

Poke Pointer Profile Example

3D GameObject

可以通过两种不同的方式向 3D GameObject 添加触摸交互,具体取决于你的 3D 对象是否应该只有一个可触摸平面,或者它是否应该基于整个碰撞体可触摸。 第一种方法通常用于具有 BoxCollider 的对象,在这种情况下,你希望碰撞体只有单个面对触摸事件做出反应。 另一种方法适用的对象需要是从任何方向都可触摸的对象,具体取决于其碰撞体的情况。

单面触摸

这对于只有单面需要可触摸的情况很有用。 此选项假设游戏对象具有 BoxCollider。 可以将其与非 BoxCollider 对象一起使用,在这种情况下,需要手动设置“边界”和“本地中心”属性以配置可触摸平面(即边界应设置为非零值)。

  1. 在应可触摸的 GameObject 上,添加 BoxCollider 和 NearInteractionTouchable 组件。

    1. 如果在下面的组件脚本中使用 IMixedRealityTouchHandler 接口,请将“要接收的事件”设置为“触摸”

    2. 单击“修复边界”和“修复中心”

    NearInteractionTouchable Setup

  2. 在该对象或其任一上级上,添加一个脚本组件,用于实现 IMixedRealityTouchHandler 接口。 具有 NearInteractionTouchable 的对象的任一上级也将能够接收指针事件。

注意

在选择了 NearInteractionTouchable GameObject 的编辑器场景视图中,请注意白色边框正方形和箭头。 箭头指向可触摸的“前面”。 碰撞体只能从该方向触摸。 要使碰撞体从各个方向均可触摸,请参阅有关任意碰撞体触摸的部分。 NearInteractionTouchable Gizmos

任意碰撞体触摸

这适用于游戏对象需要整个碰撞体面都可触摸的情况。 例如,这可用于具有 SphereCollider 的对象的触摸交互,在这种情况下,整个碰撞体都需要可触摸。

  1. 在应可触摸的 GameObject 上,添加一个碰撞体和 NearInteractionTouchableVolume 组件。

    1. 如果在下面的组件脚本中使用 IMixedRealityTouchHandler 接口,请将“要接收的事件”设置为“触摸”
  2. 在该对象或其任一上级上,添加一个脚本组件,用于实现 IMixedRealityTouchHandler 接口。 具有 NearInteractionTouchable 的对象的任一上级也将能够接收指针事件。

Unity UI

  1. 在场景中添加 UnityUI 画布/确保场景中有该画布。

  2. 在应可触摸的 GameObject 上,添加一个 NearInteractionTouchableUnityUI 组件。

    1. 如果在下面的组件脚本中使用 IMixedRealityTouchHandler 接口,请将“要接收的事件”设置为“触摸”
  3. 在该对象或其任一上级上,添加一个脚本组件,用于实现 IMixedRealityTouchHandler 接口。 具有 NearInteractionTouchableUnityUI 的对象的任何上级也将能够接收指针事件。

重要

如果对象位于重叠的画布对象上,则对象的行为可能会与预期不一致。 为确保行为一致,切勿在场景中重叠画布对象。

重要

NearInteractionTouchable 脚本组件上,对于“要接收的事件”属性,有两个选项:“指针”和“触摸”。 如果在响应/处理输入事件的组件脚本中使用 IMixedRealityPointerHandler 接口,则将“要接收的事件”设置为“指针”,如果使用 IMixedRealityTouchHandler 接口,则将其设置为“触摸”

触摸代码示例

下面的代码演示了一个 MonoBehaviour,它可以附加到具有 NearInteractionTouchable 变体组件并响应触摸输入事件的 GameObject。

public class TouchEventsExample : MonoBehaviour, IMixedRealityTouchHandler
{
    public void OnTouchStarted(HandTrackingInputEventData eventData)
    {
        string ptrName = eventData.Pointer.PointerName;
        Debug.Log($"Touch started from {ptrName}");
    }
    public void OnTouchCompleted(HandTrackingInputEventData eventData) {}
    public void OnTouchUpdated(HandTrackingInputEventData eventData) { }
}

近距交互脚本示例

触摸事件

此示例创建一个立方体,使其可触摸,并在被触摸后更改颜色。

public static void MakeChangeColorOnTouch(GameObject target)
{
    // Add and configure the touchable
    var touchable = target.AddComponent<NearInteractionTouchableVolume>();
    touchable.EventsToReceive = TouchableEventType.Pointer;

    var material = target.GetComponent<Renderer>().material;
    // Change color on pointer down and up
    var pointerHandler = target.AddComponent<PointerHandler>();
    pointerHandler.OnPointerDown.AddListener((e) => material.color = Color.green);
    pointerHandler.OnPointerUp.AddListener((e) => material.color = Color.magenta);
}

抓取事件

下面的示例展示了如何使 GameObject 可拖动。 假设游戏对象上有一个碰撞体。

public static void MakeNearDraggable(GameObject target)
{
    // Instantiate and add grabbable
    target.AddComponent<NearInteractionGrabbable>();

    // Add ability to drag by re-parenting to pointer object on pointer down
    var pointerHandler = target.AddComponent<PointerHandler>();
    pointerHandler.OnPointerDown.AddListener((e) =>
    {
        if (e.Pointer is SpherePointer)
        {
            target.transform.parent = ((SpherePointer)(e.Pointer)).transform;
        }
    });
    pointerHandler.OnPointerUp.AddListener((e) =>
    {
        if (e.Pointer is SpherePointer)
        {
            target.transform.parent = null;
        }
    });
}

有用的 API

另请参阅