控制器、指针和焦点 - MRTK2

控制器、指针和焦点是较高级别的概念,它们构建在核心输入系统所建立的基础之上。 它们共同提供了与场景中对象交互的大部分机制。

Controllers

控制器是物理控制器(6 自由度、关节手等)的表示形式。 它们由设备管理器创建,负责与相应的底层系统通信,并将该数据转换为 MRTK 造型的数据和事件。

例如,在 Windows Mixed Reality 平台上,WindowsMixedRealityArticulatedHand 是一个控制器,它负责与底层 Windows 手部跟踪 API 交互以获取有关手部关节、姿势和其他属性的信息。 它负责将此数据转换为相关的 MRTK 事件(例如,通过调用 RaisePoseInputChanged 或 RaiseHandJointsUpdated 来这样做),并更新自身的内部状态,以便对 TryGetJointPose 的查询返回正确的数据。

一般情况下,控制器的生命周期包括:

  1. 在检测到新源时(例如,设备管理器检测到手部并开始对其进行跟踪),设备管理器会创建控制器。

  2. 在控制器的 Update() 循环中,它将调用其底层 API 系统。

  3. 在同一更新循环中,它通过直接调用核心输入系统本身来引发输入事件更改(例如,引发 HandMeshUpdated 或 HandJointsUpdated)。

指针和焦点

指针用于与游戏对象交互。 本部分介绍如何创建和更新指针,以及它们如何确定聚焦的对象。 此外还会介绍存在的不同类型的指针,以及在哪些情况下它们处于活动状态。

指针类别

指针通常属于以下类别之一:

  • 远指针

    这些类型的指针用于与远离用户的对象进行交互(“远离”简单地定义为“不近”)。 这些类型的指针通常会投射出可以深入世界的线条,使用户能够对不在他们身边的对象进行交互和操作。

  • 近指针

    这些类型的指针用于与距离用户足够近的对象进行交互,以便可以抓取、触摸和操作对象。 一般情况下,这些类型的指针通过查找附近区域中的对象来与对象交互(在短距离处进行光线投射、进行球体投射、查找附近的对象,或枚举被视为可抓取/可触摸对象的对象列表)。

  • 瞬移指针

    这些类型的指针插入到瞬移系统,以处理将用户移到指针所指位置的操作。

指针调解

由于单个控制器可以有多个指针(例如,关节手可以同时具有近距和远距交互指针),因此有一个组件负责调解哪个指针应处于活动状态。

例如,当用户的手部接近某个可按按钮时,ShellHandRayPointer 应停止显示,而 PokePointer 应该参与进来。

这由 DefaultPointerMediator 处理,它负责根据所有指针的状态确定哪些指针处于活动状态。 此措施的关键之处在于,当近指针靠近对象时禁用远指针(请参阅 DefaultPointerMediator)。

通过更改指针配置文件中的 PointerMediator 属性,可以提供指针调解器的替代实现。

如何禁用指针

由于指针调解器每帧都会运行,因此它最终会控制所有指针的活动/非活动状态。 因此,如果在代码中设置指针的 IsInteractionEnabled 属性,每帧的指针调解器会覆盖此属性值。 你可以改为自行指定 PointerBehavior 来控制指针的打开和关闭。 请注意,仅在 MRTK 中使用默认的 FocusProviderDefaultPointerMediator 时才能执行此操作。

示例:在 MRTK 中禁用手部射线

以下代码将关闭 MRTK 中的手部射线:

// Turn off all hand rays
PointerUtils.SetHandRayPointerBehavior(PointerBehavior.AlwaysOff);

// Turn off hand rays for the right hand only
PointerUtils.SetHandRayPointerBehavior(PointerBehavior.AlwaysOff, Handedness.Right);

以下代码将手部射线恢复为它们在 MRTK 中的默认行为:

PointerUtils.SetHandRayPointerBehavior(PointerBehavior.Default);

以下代码将强制打开手部射线,无论是否靠近可抓取对象:

// Turn off all hand rays
PointerUtils.SetHandRayPointerBehavior(PointerBehavior.AlwaysOn);

有关更多示例,请参阅 PointerUtilsTurnPointersOnOff

FocusProvider

FocusProvider 是负责迭代所有指针的列表并找出每个指针的聚焦对象的主类。

在每个 Update() 调用中,此类将:

  1. 更新所有的指针,更新方式是投射光线,并按照指针本身的配置执行命中检测(例如,球体指针可能指定 SphereOverlap raycastMode,因此 FocusProvider 将执行基于球体的碰撞)

  2. 基于指针更新聚焦对象(即,如果某个对象获得焦点,则也会触发该对象的事件;如果某个对象失去焦点,则会触发失去焦点的操作,等等)。

指针配置和生命周期

可以在输入系统配置文件的 Pointers 节中配置指针

指针的生命周期通常如下:

  1. 设备管理器检测某个控制器是否存在。 然后,此设备管理器通过调用 RequestPointers 来创建与该控制器关联的指针集。

  2. FocusProvider 会在其 Update() 循环中迭代所有有效指针,并执行关联的光线投射或命中检测逻辑。 这用于确定每个特定指针聚焦的对象。

    • 由于可以同时激活多个输入源(例如,两只手都处于活动状态),因此也有可能多个对象同时具有焦点。
  3. 设备管理器在发现控制器源丢失时,会摧毁与丢失的控制器关联的指针。