当前位置: 首页 > news >正文

Unity VR:XR Interaction Toolkit 输入系统(Input System):获取手柄的输入

文章目录

  • 📕教程说明
  • 📕Input System 和 XR Input Subsystem(推荐 Input System)
  • 📕Input Action Asset
    • ⭐Actions Maps
    • ⭐Actions
    • ⭐Action Properties
      • 🔍Action Type (Value, Button, Pass through)
    • ⭐Binding Properties
      • 🔍Path
      • 🔍Control Scheme
      • 🔍Interactions
      • 🔍Processors
    • ⭐总结
  • 📕如何使用 Input System
    • ⭐XR Interaction Toolkit 现有脚本调用 Input System 的原理
      • 🔍Input Action Manager 脚本
      • 🔍XR Controller (Action-based) 脚本
    • ⭐在自己的脚本中引用 Input System 中的动作
      • 🔍直接引用 Input Action
      • 🔍引用 InputActionReference(推荐)
      • 🔍引用 InputActionProperty(推荐)
      • 🔍引用 InputActionMap
      • 🔍引用 Input Action Asset(推荐)
    • ⭐将动作与动作触发时执行的方法进行关联
      • 🔍方法一:将执行的方法绑定到 performed 委托上(推荐)
      • 🔍方法二:通过 action.triggered 判断是否触发
    • ⭐获取输入的值(ReadValue\<T>)
    • ⭐在 Input Action Asset 中添加自定义的动作输入映射
  • 📕总结

输入系统是 VR 应用中非常重要的一部分。我们通常需要获取 VR 手柄上某个按键的输入,然后将其作用到应用中,比如按下手柄的 Grip 键进行抓取。在我的其他 Unity XR Interaction Toolkit 的开发教程里,已经有介绍如何去获取手柄的输入。那么这篇教程我将做一个总结,将相关的重点集中在一个教程里。


📕教程说明

使用的 Unity 版本: 2021.3.5

使用的 VR 头显: Oculus Quest 2

教程使用的 XR Interaction Toolkit 版本:2.3.2(此教程尽量考虑了向上兼容,如果有过期的地方,欢迎大家指出)

OpenXR+XR Interaction Toolkit 的环境配置可以参考这篇教程:Unity VR 开发教程 OpenXR+XR Interaction Toolkit (一) 安装和配置


📕Input System 和 XR Input Subsystem(推荐 Input System)

在 XR Interaction Toolkit 中,有两种接收输入的方式。一种是 Action-based,另一种是 Device-based,它们分别对应两种不同的输入系统,Action-based 使用的是 Unity 中的 InputSystem,Device-based 使用的是 Unity 中的 XR Input Subsystem,区别可以参考我的这篇文章:Unity XR Interaction Toolkit 组件解析(一)Action-based 和 Device-based 的区别
现在,XR Interaction Toolkit 官方推荐使用的是基于 Action-based 的 Input System。因此,我的系列教程中使用的也都是 Input System。

Input System 是 Unity 提供的一套更方便,拓展性更高的用于检测键盘,手柄,鼠标,摇杆等设备输入的系统。它将输入设备和动作逻辑互相分离,通过配置映射来处理输入信息。也就是说,我们可以在 Input System 中定义一个动作,这个动作可以是一至多个输入操作的映射,也可以说这个动作能够绑定一至多个设备输入的操作。在 Unity 中,我们只需要提前配置好动作和输入的映射关系,无需写代码来判断设备输入,只需把重心放在逻辑处理上,也就是检测到输入后要做的事情。举个例子,我要在按下手柄 Grip 键的时候触发抓取,那么我可以在 Input System 的配置文件中定义“抓取”这个动作,将“按下手柄 Grip 键”这个输入操作与“抓取”动作进行绑定,此时动作和输入就成了映射关系。然后在代码中我们不需要监听“按下手柄 Grip 键”什么时候触发,只需要编写按下手柄 Grip 键后触发的抓取逻辑,将其与我们定义的动作关联。这样,Input System 会自动帮我们监听“按下手柄 Grip 键”的输入操作,如果触发了,则说明与输入操作绑定的动作触发了,这时候程序就会执行我们编写的抓取逻辑。

使用这种方式的好处是:

  1. 我们在代码中关心的是定义的动作触发后需要执行什么样的方法,而不用关心设备的输入是否触发,设备的输入由 Input System 自动监听。
  2. 以后我如果想把需求改为“按下手柄 Trigger 键进行抓取”,那么我只需要在配置文件里将“按下手柄 Trigger 键”的输入操作与“抓取”动作绑定,形成映射关系,删除原来“按下手柄 Grip 键”的绑定,而代码不需要做任何改变。因为在代码中我们是将与输入绑定的动作与触发的逻辑方法进行了关联,此时抓取需要做的事情关联的仍然是“抓取”这个动作,只不过我们在配置文件中将与“抓取”动作绑定的输入操作改为了“按下手柄 Trigger 键”。因此,我们在代码中关心的是动作的触发,在配置文件中关心的是动作与什么样的输入绑定,而监听输入完全交给了 Input System 本身,不用我们自己编写监听输入相关的代码,这大大提高了可拓展性。
    在这里插入图片描述

以上便是 Input System 输入系统的使用思路。如果看到这里还有点懵,不用担心,接下来我会演示具体的操作。


📕Input Action Asset

Input Action Asset 是 Input System 中的配置文件。当我们导入了 XR Interaction Toolkit 的 Starter Assets 后,可以在下图中的这个路径(我的 XRI 版本是 2.3.2)看到这个资源文件:

在这里插入图片描述

在这里插入图片描述

名字叫做 XRI Default Input Actions,它是 XR Interaction Toolkit 官方为我们准备的一个 Input Action Asset 配置文件,里面已经配置了常用的动作与输入的映射关系。我们可以双击这个文件,看看它长什么样:

在这里插入图片描述

跳出的页面从左往右可以分为三个区域:Action Maps,Actions 和 Action Properties。

⭐Actions Maps

在这里插入图片描述

里面的每一行相当于一个动作输入映射集,称为 Input Action Map。

⭐Actions

在这里插入图片描述

里面的每一行相当于当前输入映射集里定义的动作输入映射。最左边为绿色的代表动作(Action),每个动作可以添加下一个层级,也就是最左边是蓝色的这些东西,它们代表该动作绑定的输入操作。如上图所示,比如 Select 是一个动作,这个动作绑定了 gripPressed [LeftHand XR Controller] 和 indexPressed [LeftHand MetaAimHand] 两个输入操作。

⭐Action Properties

在这里插入图片描述

这个相当于某一个动作的属性面板,选中 Actions 下的某一个动作就能看到最右边的这个 Action Properties 面板。用的比较多的是 Action Type,可以看到它有三种类型:Value,Button,Pass through

在这里插入图片描述

🔍Action Type (Value, Button, Pass through)

Value 是一个值,可以代表连续的状态变化。比如说手柄 Grip 键按下的程度,没按的时候值为 0;逐渐按下最终按到底,值会逐渐增大到 1。再比如手柄摇杆推动的程度,也是需要用 Value 作为 Action Type。

Button 就是表示按钮或者按键触发的一次性动作。只有“按下”和“没按下”两种状态。

Pass through 和 Value 类似。它们的区别按官方的说法是如果动作绑定了多个输入,Value 会切换到最主要的那一个输入,也就是当某一个输入的值大于当前正在驱动动作的那一个输入,值更大的那个输入会接管驱动动作的权利。而 Passthrough 面对多个输入时,只会让最近发生的那个输入驱动动作。这种一个动作绑定多个输入,由输入驱动动作的情况可能会在一个 PC 游戏连接了多个游戏手柄的时候发生,因为需要确保哪一个游戏手柄拥有主要控制权。而在 VR 开发中也许不是很常见,在日常开发中,Value 和 Button 用的比较多。

⭐Binding Properties

官方文档:https://docs.unity3d.com/Packages/com.unity.inputsystem@1.4/manual/ActionBindings.htmll

在这里插入图片描述

我们回到 Actions 这一列,每个动作下绑定的东西(最左边为蓝色图标)叫做 Binding,也就是与动作形成映射关系的输入操作。以 XRI LeftHand Interaction 下的 Select 动作为例(上图所示),先看一下 Select 动作的 Action Properties 面板:

在这里插入图片描述

可以看出这是一个 Button 类型,表示某个按钮是否被按下。然后点击 Select 动作下的 gripPressed [LeftHand XR Controller],可以看到最右边的面板变成了下图所示的样子:

在这里插入图片描述

从上到下分为 Path,Use in control scheme,Interactions 和 Processors

🔍Path

Path 用一个路径代表具体的输入操作,上图中表示的是“按下左手柄的 Grip 键”这一个输入操作,也就是说 Select 动作与 “按下左手柄的 Grip 键”这一输入进行了绑定。点开这个按钮可以看到系统内置了一些常用的输入设备,这一块会在后面的实战过程中再次讲解。

在这里插入图片描述

🔍Control Scheme

Path 参数的下方有一个 Use in control scheme,然后有一些选项可以勾选:

在这里插入图片描述

Control Scheme 只是为了给输入的 Binding 进行分组分类,图中的三个选项是 XRI 为我们预置的分类。我们可以看到整个 Input Action Asset 面板左上角的 All Control Schemes,然后点开它:

在这里插入图片描述
它相当于一个过滤器。当选中了 All Control Schemes(所有类别)时,XRI LeftHand Locomotion 下的 Actions 这一列如下图所示:

在这里插入图片描述
但是如果我将 Control Schemes 改成 Continous Move,Actions 这一列就会发生变化:

在这里插入图片描述

可以看到此时就只有 Move 动作下显示了 Binding(Primary2DAxis [LeftHand XR Controller]),而其他的 Binding 被过滤掉了。我们可以看一下 Primary2DAxis [LeftHand XR Controller] 的 Binding Properties 面板:

在这里插入图片描述
因为 Use in control scheme 下的 Continuous Move 被勾选了,所以这个 Binding 属于 Continuous Move 这个类别。而 XRI LeftHand Locomotion 下其他动作的 Binding 勾选了其他的 Control Scheme 类别,所以当我们的 Input Action Asset 面板的 Control Scheme 过滤器选择了 Continous Move,当前 Input Action Map 下属于 Continous Move 类别的 Binding 会显示在 Actions 面板中,而其他类别的 Binding 会被过滤掉。

当然,我们也可以自定义 Control Scheme 类别:

在这里插入图片描述

在这里插入图片描述

🔍Interactions

Interactions 对输入的方式进行了更详细的规范。

在这里插入图片描述

点击 Interactions 右侧的“+”号,可以看到系统为我们提供了几个输入方式。如果你想要详细了解它们的区别,可以参考官方文档:https://docs.unity3d.com/Packages/com.unity.inputsystem@1.4/manual/Interactions.html
在这里插入图片描述

这里需要注意的是,如果动作的 Action Type 选择了 Value,然后 Control Type 选择了 Delta/Dpad/Stick/Vector2 中的其中一个,Binding Properties 下的 Interactions 会多出一个 Sector 选项。举个例子,我们找到 XRI LeftHand Locomotion 下的 Teleport Select Activate 动作和与其绑定的 Primary2DAxis [LeftHand XR Controller],如下图所示:

在这里插入图片描述

先看一下 Teleport Select Activate 动作的 Action Properties 面板:

在这里插入图片描述

与之前举例的 Select 动作不同的是,Select 动作的 Action Type 是 Button,而 Teleport Select Activate动作的 Action Type 是 Value,并且 Control Type 是 Vector2,也就是说接收的输入是一个二维向量。

然后查看 Primary2DAxis [LeftHand XR Controller] 的 Binding Properties 面板:

在这里插入图片描述

在这里插入图片描述

可以看到这个输入 Binding 已经添加了一个名叫 Sector 的 Interaction,而这个 Sector 选项也是新增的。Sector 是扇区的意思,手柄摇杆的相关功能经常要用到 Sector 这个 Interaction。因为摇杆是在一个圆形范围内运动,我们可以将其想象为 x-y 坐标系下的一个圆心在原点,半径为 1 的圆:

在这里插入图片描述
在 Primary2DAxis [LeftHand XR Controller] 中, Sector 的 Direction 为 North,那么对应的就是摇杆向前推动的输入。我们可以想象成摇杆向下图红色区域推动时会触发这个输入操作:

在这里插入图片描述
在 VR 中向前推动手柄摇杆触发传送的操作比较常见,因此 Teleport Select Activate 这个动作经常用于激活传送。因为 Teleport Select Activate 动作本身的 Action Type 是 Value,Control Type 是 Vector2,它的类型是一个二维向量,该动作绑定的输入操作 Primary2DAxis [LeftHand XR Controller] 接收的是手柄摇杆的输入,用一个二维向量表示,但是接收的是摇杆各个方向的输入,所以要对输入的方向做个限制,只有摇杆向前方的一小段扇区推动才能生效。于是需要在 Binding Properties 中的 Interactions 添加 Sector 类型,将 Direction 改为 North。

🔍Processors

Processors 处理器可以对接收的输入数据进行处理,返回一个新的数据。
举个例子,我们找到 XRI LeftHand Locomotion 下的 Move 动作,查看它绑定的输入的 Binding Properties 面板:

在这里插入图片描述
在这里插入图片描述

可以看到它的 Processors 添加了一个 Stick DeadZone,它有一个最小值和最大值,因为这个 Binding 接收的是一个来自摇杆的 Vector2 类型的二维向量数据,当向量的大小(magnitude)小于 Min 值时,会将结果视为(0,0),当向量的大小大于 Max 值时,会将向量的大小归一化为 1【比如(1,1)(1,-1)等】。

除此之外,还有其他类型的 Processor 可以选择,当动作 Action 的 Action Type 不同时,供选择的 Processor 也有所不同:

在这里插入图片描述

每个 Processor 的作用可以参考官方文档:https://docs.unity3d.com/Packages/com.unity.inputsystem@1.4/manual/Processors.html

在这里插入图片描述

⭐总结

总结一下 Input System 的结构。首先需要一个 Input Action Asset 配置文件,里面包含了 Action Maps 动作输入映射集,每一个映射集包含了 Actions 动作,每个动作绑定一至多个 Binding 输入。根据我们前面介绍的,我们只会在配置文件中配置动作和输入的映射关系,而在代码中,我们需要引用 Action 动作。因此,我们需要记住 Input Action Asset => Action Map => Action 的结构,这对我们后续的代码实战有帮助。


📕如何使用 Input System

当我们在 Input Action Asset 配置文件中配置了动作与输入的映射关系后,我们可以在脚本中访问 Input Action Asset 中配置的动作。一种使用场景是在代码中编写接收到输入后执行的方法,然后将其与 Input System 中相应的动作关联,这样才能访问我们的配置文件,当系统监听到输入时,视为与其绑定的动作触发,从而触发我们编写的方法。另一种使用场景是获取输入的值,比如按下手柄 Grip 键来驱动手部动画,需要根据 Grip 键按下的程度来驱动动画的切换。

⭐XR Interaction Toolkit 现有脚本调用 Input System 的原理

🔍Input Action Manager 脚本

当我们在场景中添加 XR Origin(VR) 这个物体的时候,XR Origin 物体上会自动添加一个 Input Action Manager 脚本,相当于输入动作的管理者:

在这里插入图片描述

可以看到 XRI 提供的 Input Action Asset(名字叫做 XRI Default Input Actions)被自动添加到了脚本的 Action Assets 中。

因为 Input System 中的 Actions 动作默认是关闭的,也就是系统默认不会监听我们配置的输入,需要手动 Enable 这些 Action 才会开始监听。而 Input Action Manager 脚本可以帮我们激活添加到 Action Assets 中的动作,所以 XRI Default Input Actions 配置文件中的所有动作会被 Input Action Manager 激活,那么系统也会开始监听这些动作所绑定的输入

我们可以打开 Input Action Manager 脚本:

在这里插入图片描述

在这里插入图片描述

可以看到在 OnEnable 的时候,脚本调用 EnableInput 方法,调用存储的 Input Action Asset 的 Enable 方法,因此 Input Action Asset 中的所有 Action 都会被激活,所有 Action 绑定的输入会被系统监听。

所以,后续我们如果在脚本中引用一个动作,如果该动作所属的 Input Action Asset 被添加到了 Input Action Manager 中,我们是不需要手动在脚本中激活该动作的。

🔍XR Controller (Action-based) 脚本

在 XR Origin 下的 LeftHand Controller 和 RightHand Controller 物体上挂载了 XR Controller(Action-based) 脚本。它能将 Input Action Asset 中设定的动作与 VR 中的交互关联起来,以左手为例:

在这里插入图片描述

可以看到在 Reference 中引用了 XRI Default Input Actions 中配置的一些动作,类型为 Input Action Reference。这个脚本通常要和交互功能结合在一起,才能在监听到设备输入的时候触发交互的功能。VR 中的交互由两部分组成:发起交互的对象(Interactor)和可被交互的对象(Interactable)。我们以抓取功能为例,Interactor 相当于虚拟的双手,对应着手柄,Interactable 就是可以被抓取的物品。在 Unity 中,我们需要给手部添加 XR Direct Interactor 脚本,给物品添加 XR Grab Interactable 脚本(具体的使用方法可以参考这篇教程:Unity VR 开发教程 OpenXR+XR Interaction Toolkit (六)手与物品交互(触摸、抓取)),这样抓取交互的条件就满足了,接下来需要解决的是手柄输入触发抓取的问题。假设按下手柄的 Grip 键触发抓取,刚好我们 XR Controller 脚本中的 Select Action 引用的 Reference 是 Select 动作:

在这里插入图片描述
在这里插入图片描述

Select 动作绑定的就是“按下手柄 Grip 键”的输入操作,因此当系统监听到“按下手柄 Grip 键”的输入操作后,便会触发 XR Controller 的 Select Action。而 在拥有 Interactor 和 Interactable 相关脚本的条件下,Select 动作的发生就会让物体被抓取到手上。这就是 XR Interaction Toolkit 现有脚本调用 Input System 的一个例子。


⭐在自己的脚本中引用 Input System 中的动作

有时候,我们需要在自己的脚本中引用 Input System 中的动作,然后编写这个动作触发后执行的方法。回想一下 Input System 的结构:Input Action Asset => Action Map => Action,我们需要获取 Action 相关的类。在 Input System 中,Action 所属的类叫做 Input Action,首先我们要导入 UnityEngine.InputSystem 命名空间:

using UnityEngine.InputSystem;

然后有以下几种方法在代码中获取它,后续我们也要得到 InputAction 类的变量,对它进行操作

🔍直接引用 Input Action

我们可以直接引用 Input Action 类:

public InputAction testAction;

这样我们的 Inspector 面板会多出这个东西:

在这里插入图片描述
但是这里需要我们在 Inspector 面板中配置 Action 绑定的输入操作,无法直接引用 Input Action Asset 配置文件中已经配置好的动作输入映射。所以不推荐大家直接在脚本中引用 InputAction。

在这里插入图片描述

在这里插入图片描述

🔍引用 InputActionReference(推荐)

public InputActionReference testActionReference;

这个 InputActionReference 类能够访问 Input Action Asset 中 Input Action Map 里的 Input Action
此时 Inspector 面板如下:

在这里插入图片描述

我们可以选择一个 InputActionReference:

在这里插入图片描述
这些就是我们在 Input Action Asset 配置文件里配置的动作。

有了 InputActionReference 类的引用,我们可以通过

testActionReference.action

得到 InputAction 类的变量。

🔍引用 InputActionProperty(推荐)

public InputActionProperty testActionProperty;

此时 Inspector 面板如下图所示:

在这里插入图片描述
InputActionProperty 既能够引用 Input Action Asset 中的动作配置,也能够在面板中定义动作和输入的映射关系。

如果我们勾选了 Use Reference,可以在面板中给 Reference 变量赋值,选择配置文件中配置好的动作。

在这里插入图片描述

如果没有勾选 Use Reference,我们也可以像直接引用 InputAction 后看到的面板那样,在 Inspector 面板中配置动作和输入的映射。

在这里插入图片描述

不过还是推荐大家统一在 Input Action Asset 配置文件中配置动作和输入的映射关系,这样便于管理。

和 InputActionReference 一样,我们可以通过:

testActionProperty.action

得到 InputAction 类的变量。

🔍引用 InputActionMap

public InputActionMap testActionMap;

因为 InputAction 的上一层级是 InputActionMap,所以我们也能通过 InputActionMap 来获取 InputAction

此时 Inpsector 面板如图所示:

在这里插入图片描述

这里我们就只能在 Inspector 面板中配置动作和输入的映射。和之前直接引用 InputAction 不同,如果直接引用 InputAction,相当于我们定义了一个动作,然后在面板中配置这个动作绑定的输入;如果引用 InputActionMap,因为 Action Map 是一组动作输入映射集,所以我们能在面板中定义一组动作,然后为每一个动作配置绑定的输入。

接下来我们可以在代码中通过:

InputAction[] actions = testActionMap.actions.ToArray(); 

获取一组 InputAction 变量。

但是引用 InputActionMap 同样也不能访问 Input Action Asset,所以也不推荐这种用法。

🔍引用 Input Action Asset(推荐)

public InputActionAsset testActionAsset;

这时候 Inspector 面板如下:

在这里插入图片描述

然后我们可以用 XRI Default Input Actions 文件进行赋值:

在这里插入图片描述

结合 Input System 的结构,我们可以根据一个 Input Action Asset 找到其中一个 Input Action Map,然后在这个 Input Action Map 中找到一个 Input Action。所以代码可以这么写:

 InputAction select = testActionAsset.FindActionMap("XRI LeftHand Interaction").FindAction("Select");

这样就能够准确地找到我们想要的动作。虽然 InputActionAsset 类有一个 FindAction 方法:

InputAction select = testActionAsset.FindAction("Select");

但是如果不同的 Input Action Map 含有相同名字的 Input Action,这个 InputActionAsset 下的 FindAction 方法会返回已经激活的 Action 中,第一个被找到的 Action。因此还是推荐先用 FindActionMap 找到具体的 Action Map,再用 FindAction 找到具体的 Action。

引用 Input Action Asset 的好处是:之前不管是引用 InputActionReference 还是 InputActionProperty,都需要手动将具体的 Action 赋值到 Inspector 面板中。如果你喜欢用纯代码的方式找到具体的 Action,可以尝试引用 Input Action Asset,然后用 FindActionMap 和 FindAction 方法找到具体的 Input Action

⭐将动作与动作触发时执行的方法进行关联

能够得到 InputAction 类的变量后,相当于连接了 Input System 和我们的脚本。接下来只剩下编写动作触发时需要执行的方法,将其与动作进行关联。这种情况适用于 Action Type 为 Button 的动作,因为只有“动作触发”和“动作没触发”两种状态,当输入发生,比如某个按键按下的时候,触发动作,执行相应的行为。

现在假设我们有一个需求:按下左手柄 Grip 键的时候在控制台输出一句话。
我这里选择引用 InputActionReference 的方式。

🔍方法一:将执行的方法绑定到 performed 委托上(推荐)

using UnityEngine;
using UnityEngine.InputSystem;public class XRInputTest : MonoBehaviour
{public InputActionReference testActionReference;//public InputActionProperty testActionProperty;//public InputActionMap testActionMap;//public InputActionAsset testActionAsset;private InputAction testAction;void Start(){testAction = testActionReference.action;testAction.performed += ActivateBehavior;}private void ActivateBehavior(InputAction.CallbackContext context){print("执行方法");}
}

我们需要将动作触发时执行的方法绑定到 InputAction 类的 performed 委托中:

testAction.performed += ActivateBehavior;

performed 委托会在动作触发完成后调用,它是一个
Action<InputAction.CallbackContext> 类型的委托,所以绑定的方法需要将 InputAction.CallbackContext 作为方法参数

因为我的需求是监听“按下手柄 Grip 键”的操作,这与 XRI Default Input Actions 配置文件中的 XRI LeftHand Interaction (Action Map)下的 Select 动作(Action)相匹配。所以我们在 Inspector 面板中进行赋值:

在这里插入图片描述
运行程序,当我们按下左手柄 Grip 键的时候,如果在控制台能看到输出,说明我们成功了。

在这里插入图片描述

🔍方法二:通过 action.triggered 判断是否触发

我们也可以这样写代码:

using UnityEngine;
using UnityEngine.InputSystem;public class XRInputTest : MonoBehaviour
{public InputActionReference testActionReference;//public InputActionProperty testActionProperty;//public InputActionMap testActionMap;//public InputActionAsset testActionAsset;private InputAction testAction;void Start(){testAction = testActionReference.action;}private void Update(){if (testAction.triggered){print("执行方法");}}}

testAction.triggered 是一个 bool 变量,当动作触发时,返回 true,这时候可以执行动作触发时做的事情。但是这种写法将要过时,还是推荐大家使用第一种方法。

⭐获取输入的值(ReadValue<T>)

这种情况一般适用于 Action Type 为 Value 或者 Pass through 的动作。以获取按下左手柄 Grip 键的程度为例,我想获取按下左手柄 Grip 键对应的值,可以这么做:

using UnityEngine;
using UnityEngine.InputSystem;public class XRInputTest : MonoBehaviour
{public InputActionReference leftGripActionValueReference;private InputAction leftGripActionValue;void Start(){leftGripActionValue = leftGripActionValueReference.action;}private void Update(){GetGripValue();}private void GetGripValue(){float value = leftGripActionValue.ReadValue<float>();print($"value:{value}");}   
}

通过 InputAction 类下的 ReadValue<T> 方法获取输入的值,Grip 键按下的程度可以用一个 float 类型的数来表示。

在 XRI Default Input Actions 配置文件中,XRI LeftHand Interaction (Action Map)下的 Select Value 动作(Action)能获取按下左手柄 Grip 键对应的值,该动作的 Action Type 为 Value

在这里插入图片描述

在这里插入图片描述

⭐在 Input Action Asset 中添加自定义的动作输入映射

接下来我将演示如何在 在 Input Action Asset 中添加自定义的动作输入映射。

我使用的是 Quest 2,那么就来演示一下按下左手柄 X 键时控制台输出一句话。

首先打开 XRI Defalut Input Actions 文件,我们直接在官方为我们准备的 Input Action Asset 里操作。因为配置文件中默认没有“按下左手柄 X 键”相关的动作,所以我们需要手动添加自定义的动作。我选择在 XRI LeftHand Interaction 这个 Action Map 中添加一个新的动作,将它命名为 PrimaryButton。

在这里插入图片描述
确保右侧面板的 Action Properties 中的 Action Type 为 Button,因为我们检测的是按键是否被按下。

在这里插入图片描述
接着点击动作下方的这个 Binding,点击右侧面板的 Path,选择 XR Controller:

在这里插入图片描述

选择 XR Controller (LeftHand):

在这里插入图片描述

选择 Optional Controls:

在这里插入图片描述

选择 primaryButton,这个 primaryButton 对应左手柄的 X 键或者右手柄的 A 键;而 secondaryButton 对应 左手柄的 Y 键或者右手柄的 B 键。大家也可以自行尝试 Optional Controls 里的其他选项,基本涵盖了手柄的所有输入。

在这里插入图片描述

最后别忘了点击面板上方的 Save Asset 进行保存:

在这里插入图片描述

然后编写相关代码:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;public class XRInputTest : MonoBehaviour
{public InputActionReference testActionReference;    private InputAction testAction;void Start(){testAction = testActionReference.action;testAction.performed += ActivateBehavior; }private void ActivateBehavior(InputAction.CallbackContext context){print("执行方法");}    
}

在 Inspector 面板中赋值:

在这里插入图片描述

现在运行程序,当我按下左手柄的 X 键时,控制台就会输出一段语句:

在这里插入图片描述


📕总结

XR Interaction Toolkit 推荐使用 Unity 的 Input System 来接收设备的输入。它将输入设备和动作逻辑互相分离,通过配置映射来处理输入信息。我们只需要提前配置好动作和输入的映射关系,在代码中把重心放在逻辑处理上,也就是检测到输入后要做的事情,将其与配置的动作相关联。而设备输入的监听交给了系统本身。当监听到设备输入时,视为触发了输入所绑定的动作,便会触发动作发生后需要做的事情。

总的来说使用起来分为这几个阶段:编辑配置文件,关联脚本与配置文件中的动作,编写接收到输入时执行的逻辑

Input System 需要有一个 Input Action Asset 配置文件,Input Action Asset 可以存储多个 Input Action Map,每一个 Action Map 相当于一组动作输入映射集,每一个 Action Map 存储的动作(Input Action)能够绑定一至多个输入操作。

配置好动作和输入的映射关系后,我们需要在脚本中获取 InputAction 类的变量,才能访问配置文件中对应的动作。然后可以将动作触发时执行的方法绑定到 InputAction 类的委托上,这样就能在接收到设备输入时执行相应的逻辑;或者使用 InputAction 类的 ReadValue<T> 方法获取输入的值。

相关文章:

Unity VR:XR Interaction Toolkit 输入系统(Input System):获取手柄的输入

文章目录 &#x1f4d5;教程说明&#x1f4d5;Input System 和 XR Input Subsystem&#xff08;推荐 Input System&#xff09;&#x1f4d5;Input Action Asset⭐Actions Maps⭐Actions⭐Action Properties&#x1f50d;Action Type (Value, Button, Pass through) ⭐Binding …...

智慧工地一体化云平台源码:监管端、工地端、危大工程、智慧大屏、物联网、塔机、吊钩、升降机

智慧工地解决方案依托计算机技术、物联网、云计算、大数据、人工智能、VR&AR等技术相结合&#xff0c;为工程项目管理提供先进技术手段&#xff0c;构建工地现场智能监控和控制体系&#xff0c;弥补传统方法在监管中的缺陷&#xff0c;最终实现项目对人、机、料、法、环的全…...

C# 表达式体方法 C#算阶乘

//表达式体方法private int Add(int a, int b) > a b;[Fact]public void Test(){var result1 Factorial(1);//1var result2 Factorial(2);//2var result3 Factorial(3);//6var result4 Factorial(4);//24var result5 Factorial(5);//120var result6 Add(100, 200);//…...

互联网发展历程:保护与隔离,防火墙的安全壁垒

互联网的快速发展&#xff0c;不仅带来了便利和连接&#xff0c;也引发了越来越多的安全威胁。在数字时代&#xff0c;保护数据和网络安全变得尤为重要。然而&#xff0c;在早期的网络中&#xff0c;安全问题常常让人担忧。 安全问题的困扰&#xff1a;网络威胁日益增加 随着互…...

基于IMX6ULLmini的linux裸机开发系列七:中断处理流程

中断上下文 cpu通过内核寄存器来运行指令并进行数据的读写处理的&#xff0c;它在进入中断前一个时刻的具体值&#xff0c;称为中断上下文 中断上下文是指CPU在进入中断之前保存的寄存器状态和其他相关信息。当CPU接收到中断请求时&#xff0c;它会保存当前正在执行的指令的状…...

Postman软件基本用法:浏览器复制请求信息并导入到软件从而测试、发送请求

本文介绍在浏览器中&#xff0c;获取网页中的某一个请求信息&#xff0c;并将其导入到Postman软件&#xff0c;并进行API请求测试的方法。 Postman是一款流行的API开发和测试工具&#xff0c;它提供了一个用户友好的界面&#xff0c;用于创建、测试、调试和文档化API。本文就介…...

react go实现用户历史登录列表页面

refer: http://ip-api.com/ 1.首先需要创建一个保存用户历史的登录的表&#xff0c;然后连接go 2.在用户登录的时候&#xff0c;获取用户的IP IP位置&#xff0c;在后端直接处理数据即可&#xff08;不需要在前端传递数据&#xff09; &#xff08;1&#xff09;增加路由&am…...

如何做好服务性能测试

一、什么是性能测试 新功能上线或切换底层数据库或扩容调优&#xff0c;根据实际业务场景的需要&#xff0c;做必要的性能压测&#xff0c;收集性能数据&#xff0c;作为上线的基准报告。 性能测试一般分一下几个阶段&#xff1a; 1. 性能测试 并发量小&#xff08;jmeter 并…...

速通蓝桥杯嵌入式省一教程:(五)用按键和屏幕实现嵌入式交互系统

一个完整的嵌入式系统&#xff0c;包括任务执行部分和人机交互部分。在前四节中&#xff0c;我们已经讲解了LED、LCD和按键&#xff0c;用这三者就能够实现一个人机交互系统&#xff0c;也即搭建整个嵌入式系统的框架。在后续&#xff0c;只要将各个功能加入到这个交互系统中&a…...

虚拟拍摄,如何用stable diffusion制作自己的形象照?

最近收到了某活动的嘉宾邀请&#xff0c;我将分享&#xff1a; 主题&#xff1a;生成式人工智能的创新实践 简要描述&#xff1a;从品牌营销、智能体、数字内容创作、下一代社区范式等方面&#xff0c;分享LLM与图像等生成式模型的落地应用与实践经验。 领域/研究方向&#xff…...

开启AI创新之旅!“华为云杯”2023人工智能应用创新大赛等你来挑战

简介 近年来&#xff0c;人工智能技术的发展如日中天&#xff0c;深刻地改变着我们的生活方式和产业格局。 为了培养AI人才&#xff0c;持续赋能AI企业&#xff0c;推进国家新一代人工智能开放创新平台建设&#xff0c;打造更加完善的AI技术创新生态&#xff0c;华为&#xf…...

npm和node版本升级教程

cmd中查看本地安装的node版本 node -v //查询node的位置 where node2.官网下载所需要的node版本&#xff0c;安装在刚查出来的文件夹下&#xff0c;即覆盖掉原来的版本 3.查看node版本是否已经更新 4.查看npm版本是否和node版本相匹配 cnpm install -g npm...

C++入门篇9---list

list是带头双向循环链表 一、list的相关接口及其功能 1. 构造函数 函数声明功能说明list(size_type n,const value_type& valvalue_type())构造的list中包含n个值为val的元素list()构造空的listlist(const list& x)拷贝构造list(InputIterator first, InputIterator…...

STM32基于CubeIDE和HAL库 基础入门学习笔记:物联网项目开发流程和思路

文章目录&#xff1a; 第一部分&#xff1a;项目开始前的计划与准备 1.项目策划和开发规范 1.1 项目要求文档 1.2 技术实现文档 1.3 开发规范 2.创建项目工程与日志 第二部分&#xff1a;调通硬件电路与驱动程序 第三部分&#xff1a;编写最基础的应用程序 第四部分&…...

Hive on Spark (1)

spark中executor和driver分别有什么作用&#xff1f; Spark中Executor 在 Apache Spark 中&#xff0c;Executor 是分布式计算框架中的一个关键组件&#xff0c;用于在集群中执行具体的计算任务。每个 Executor 都在独立的 JVM 进程中运行&#xff0c;可以在集群的多台机器上…...

PostgreSQL基本操作总结

安装按PostgreSQL数据库后&#xff0c;会默认创建用户postgres和数据库postgres&#xff0c;这个用户是超级用户&#xff0c;权限最高&#xff0c;可以创建其他用户和权限&#xff0c;在实际开发过程中&#xff0c;会新创建用户和业务数据库&#xff0c;本文主要介绍用户权限和…...

Jakarta 的 Servlet 下BeanUtils的日期处理 和JSTL 的使用

jsp优于性能等问题已经不被spring boot等支持&#xff0c;如果想使用jsp和jstl标签库需要引入一下依赖。 <!-- 用jakarta.servlet.jsp.jstl&#xff0c;用org.glassfish.web--><dependency><groupId>jakarta.servlet.jsp.jstl</groupId><art…...

聚焦电力行业CentOS迁移,麒麟信安受邀参加第六届电力信息通信新技术大会暨数字化发展论坛并发表主题演讲

为加快推进“双碳”目标下的新型能源体系和新型电力系统建设&#xff0c;深化新一代数字技术与电力业务的融合发展&#xff0c;促进电力行业关键技术自主创新、安全可控&#xff0c;助力电力企业数字化转型升级和高质量发展&#xff0c;2023年8月9-11日&#xff0c;第六届电力信…...

华为OD真题--分月饼--带答案

1. 华为OD机考题 答案 2023华为OD统一考试&#xff08;AB卷&#xff09;题库清单-带答案&#xff08;持续更新&#xff09; 2023年华为OD真题机考题库大全-带答案&#xff08;持续更新&#xff09; 2. 面试题 一手真实java面试题&#xff1a;2023年各大公司java面试真题汇总--…...

帆软大屏2.0企业制作

&#xfffc; 数字化观点中心 / 当前页 如何从0-1制作数据大屏&#xff0c;我用大白话给你解释清楚了 文 | 商业智能BI相关文章 阅读次数&#xff1a;18,192 次浏览 2023-06-08 11:51:49 好莱坞大片《摩天营救》中有这么一个场景&#xff1a; &#xfffc; 你可以看见反派大b…...

【学习笔记之opcua】使用Python获取opcua数据

Python与OPC UA的应用 示例代码 将代码放入spyder中运行后&#xff0c;出现下面这个错误 没有‘opcua’&#xff0c;那我们就下载pip install opcua 之后出现下面这个错误 问问题大不&#xff0c;安装语句写错了 正经安装语句是 !pip install opcua 读取opcua协议数据测试 …...

apache doris和StarRocks的区别

记录一下最新要用到2个新数据库的区别 Apache Doris是一个分布式的列式存储系统&#xff0c;它的设计目标是提供大规模数据处理的可靠性和高性能。Doris采用了集群方式&#xff0c;通过将数据分布在多个机器上进行处理来提高性能&#xff0c;并提供了SQL查询接口方便用户使用。…...

文心一言最新重磅发布!

8月16日&#xff0c;由深度学习技术及应用国家工程研究中心主办的WAVE SUMMIT深度学习开发者大会2023举办。百度首席技术官、深度学习技术及应用国家工程研究中心主任王海峰以《大语言模型为通用人工智能带来曙光》为题&#xff0c;阐述了大语言模型具备理解、生成、逻辑、记忆…...

css整体使用

文章目录 html与csshtml、css与排版响应式与自适应布局自适应布局响应式布局 css规则class、id、以及默认的标签名的优先级 css书写位置flex整体逻辑 bootstrap资源 html与css html负责网页功能&#xff0c;css负责网页美化&#xff1b;浏览器本身有一套默认的css样式&#xf…...

LeetCode1578. 使绳子变成彩色的最短时间

思路 拆除成本 全部拆除 - 最大的不拆除在统计成本的同时&#xff0c;维持一个成本的最大值 代码 class Solution {public int minCost(String colors, int[] neededTime) {int res 0;int i 0;int len colors.length();while (i < len) {int max -1;int sum 0;char…...

如何在机器学习中实现分类?

机器学习和统计学中的分类是一种监督学习方法,其中计算机程序从给定的数据中学习并进行新的观察或分类。在本文中,我们将详细了解机器学习中的分类。 本博客涵盖以下主题: 目录 什么是机器学习中的分类? 机器学习中的分类术语 分类算法...

华为网络篇 RIP的负载均衡-29

难度2复杂度2 目录 一、实验原理 二、实验拓扑 三、实验步骤 四、实验过程 总结 一、实验原理 RIP是使用跳数&#xff08;经过路由的数量&#xff09;作为metric值的&#xff0c;当网络上存在去往目标的路由有两条以上都是相同metric时&#xff0c;就出现了流量负载均衡。…...

前端面试的性能优化部分(10)每天10个小知识点

目录 系列文章目录前端面试的性能优化部分&#xff08;1&#xff09;每天10个小知识点前端面试的性能优化部分&#xff08;2&#xff09;每天10个小知识点前端面试的性能优化部分&#xff08;3&#xff09;每天10个小知识点前端面试的性能优化部分&#xff08;4&#xff09;每天…...

分类预测 | MATLAB实现S4VM半监督支持向量机二分类预测

分类预测 | MATLAB实现S4VM半监督支持向量机二分类预测 目录 分类预测 | MATLAB实现S4VM半监督支持向量机二分类预测分类效果基本介绍程序设计参考资料 分类效果 基本介绍 分类预测 | MATLAB实现S4VM半监督支持向量机二分类预测 程序设计 完整源码和数据获取方式&#xff1a; …...

maven -pl -am -amd

maven常见命令之 -pl -am -amd 昨天maven的deploy任务需要只选择单个模块并且把它依赖的模块一起打包&#xff0c;第一时间便想到了-pl参数&#xff0c;然后就开始处理&#xff0c;但是因为之前只看了一下命令的介绍&#xff0c;竟然花了近半小时才完全跑通&#xff0c;故记录…...

高效解决Anaconda Prompt报错Did not find VSINSTALLDIR这类问题

文章目录 回忆问题解决问题step1step2 回忆问题 类似于划红线部分然后还有很多行的报错信息&#xff0c;最后一行肯定是红色划线部分 解决问题 step1 找到 D:\Anaconda\envs\pytorch\etc\conda\activate.d在这个文件夹内会有两个文件&#xff0c;删除 vs2017_compiler_v…...

将iPhone备份到移动硬盘

文章目录 将iPhone备份到移动硬盘如何在 MacOS 上查找当前备份如何在 MacOS 上查找当前备份如何将 iPhone 备份移至外部硬盘如何永久更改 Mac系统 保存 iPhone 备份的位置更新 Mac 上的权限更改 iPhone 备份位置如何验证新的 iPhone 备份已经生效?将iPhone备份到移动硬盘 如果…...

找工作 相关资料

1、简历准备 一份近乎完美的计算机简历应该如何写以及如何修改&#xff1f;-知乎 2.1 机械面试可能的问题 2.1.1 技术邻上的 机械专业面试31问 2.1.2 B站上的 B站&#xff1a; “经常作为面试题&#xff0c;50个机械设计基础常识&#xff0c;你掌握了几个&#xff1f;”文…...

罗勇军 → 《算法竞赛·快冲300题》每日一题:“排列变换” ← 贪心算法

【题目来源】http://oj.ecustacm.cn/problem.php?id1812http://oj.ecustacm.cn/viewnews.php?id1023【题目描述】 给定一个长度为 n 的排列 a&#xff0c;需要将这个排列变成 b。 每次可以选择一个数字往左移若干个位置。 请求出最小需要移动的元素个数。【输入格式】 第一行…...

算法修炼Day51|● 309.最佳买卖股票时机含冷冻期 ● 714.买卖股票的最佳时机含手续费

LeetCode:309.最佳买卖股票时机含冷冻期 309. 买卖股票的最佳时机含冷冻期 - 力扣&#xff08;LeetCode&#xff09; 1.思路 初始化dp[i][j]数组&#xff0c;表示第i天的最大利润为dp[i][j]. 精确的定义状态是个难点&#xff0c;可以定义四种状态&#xff1a;持有股票&#…...

LVS-DR模型实例

一、LVS-DR集群介绍 LVS-DR&#xff08;Linux Virtual Server Director Server&#xff09;工作模式&#xff0c;是生产环境中最常用的一 种工作模式。 1、LVS-DR 工作原理 LVS-DR 模式&#xff0c;Director Server 作为群集的访问入口&#xff0c;不作为网关使用&#xff0…...

Vue面试题

1. vue优点 轻量级速度快简单易学低耦合可重用性独立开发文档齐全&#xff0c;且文档为中文文档 2. ## vue中组件间传值 prop/$emit 父子组件传值 ref 和 $parent/$children 父子组件传值 eventBus($emit/$on) 父子&#xff0c;隔代&#xff0c;兄弟组件传值 $attrs/$listeners…...

使用图像处理算法检测金属表面的生锈区域: Python实现及步骤解析

摘要&#xff1a; 本文主要介绍如何使用Python和OpenCV库来实现对金属表面的生锈区域的检测。图像处理在工业领域有着广泛的应用&#xff0c;尤其是对材料的表面缺陷的检测。本文将详细阐述该算法的具体实现步骤&#xff0c;并提供完整的Python代码示例。 1. 引言 金属的锈蚀是…...

通过爬虫抓取上市企业利润表并在睿思BI中展示

睿思BI从v5.3开始支持网络爬虫&#xff0c;可以从指定URL抓取表格数据&#xff0c;本示例实现从网络上抓取上市企业招商银行的利润表数据&#xff0c;并在睿思BI中进行展现。 功能演示URL&#xff1a;https://www.ruisitech.com/rsbi-ultimate/#/dashboard/ShareView?token31…...

填充柄功能

单元格右下角十字符号 顺序式填充 输入1,2&#xff0c;直接拉取即可实现顺序1到10. 复制式填充 CtrlD或者拉取&#xff0c;选择右下角复制单元格。 规律式填充 输入星期一&#xff0c;星期二&#xff0c;下拉一直可以到星期日 自定义填充 选择文件-》选项-》自定义序列 输…...

Python爬虫性能优化:多进程协程提速实践指南

目录 1. 多进程爬虫的实现&#xff1a; 1.1 将爬虫任务划分成多个子任务&#xff1a; 1.2 创建进程池&#xff1a; 1.3 执行任务&#xff1a; 1.4 处理结果&#xff1a; 代码示例 2. 协程爬虫的实现&#xff1a; 2.1 定义异步爬虫函数&#xff1a; 2.2 创建事件循环&a…...

mongodb export(2023新)

之前的mongodb export发现不能用了&#xff0c;T3带ui的版本&#xff0c;试用到期不支持导出。 根据文档&#xff0c;是因为server版本更新后 tool版本没有升级&#xff0c;(refs文档) 按文档下载bin&#xff0c;后解压到更新本地文件夹&#xff0c;替换/usr/local/bin里的文…...

css-flex使用

文章目录 flex弹性容器属性flex-directionflex-wrapflex-flowalign-itemsjustify-contentalign-content主轴和侧轴 弹性元素默认大小属性flex-growflex-shrinkalign-selfflex-basisflexorder 高度坍塌flex布局子元素宽度超出父元素 flex 弹性盒&#xff0c;伸缩盒&#xff0c;…...

SAP安全库存-安全库存共享、安全库存简介

SAP系统中的安全库存用于管理计划外和计划内的库存需求,在某些行业中,由于不同的情况,如意外损耗、损坏、环境问题、制造工艺问题、需求增加等,通常会出现意外的库存需求。 SAP提供了维护安全库存的处理方式来处理这样的问题,安全库存的字段信息在主数据视图中,在物料需…...

CentOS自己搭建时钟同步服务实操

目录 1、产生背景 2、操作过程 3、客户端操作 4、ntpd和ntpdate的区别 5、参考文章 1、产生背景 因为公司业务&#xff0c;需要使用一些网关设备上报监测实时数据&#xff0c;为了保障数据时钟一致性&#xff0c;所以需要提供一天时钟校验服务器。因为原来这个厂家的网关设…...

高阶数据结构-图

高阶数据结构-图 图的表示 图由顶点和边构成&#xff0c;可分为有向图和无向图 邻接表法 图的表示方法有邻接表法和邻接矩阵法&#xff0c;以上图中的有向图为例&#xff0c;邻接表法可以表示为 A->[(B,5),(C,10)] B->[(D,100)] C->[(B,3)] D->[(E,7)] E->[…...

Linux/Ubuntu 的日常升级和安全更新,如何操作?

我安装的是Ubuntu 20.04.6 LTS的Windows上Linux子系统版本&#xff0c;启动完成后显示&#xff1a; Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.15.90.4-microsoft-standard-WSL2 x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.c…...

Linux自动挂载U盘

文章目录 UEDV规则文件挂在U盘规则&#xff0c;创建.ruiles将下放代码放入 UEDV规则文件 规则文件是 udev 里最重要的部分&#xff0c;默认是存放在 /etc/udev/rule.d/ 下。所有的规则文件必须以".rules" 为后缀名。 下面是一个简单的规则&#xff1a; KERNEL"…...

Edge浏览器免费使用GPT3.5

搜索sider,安装Sidebar插件 注册账号即可每天免费使用30次。 Sider: ChatGPT侧边栏,GPT-4, 联网, 绘图...

面试题--redis篇

一、Redis支持的数据类型&#xff1f; String (字符串) Hash (哈希) List (列表) Set (集合) zset (sorted set&#xff1a;有序集合) 1. String&#xff08;字符串&#xff09; 格式: set key value string 类型是二进制安全的&#xff0c;意思是 redis 的 string 可以包含任…...