Unity VR开发(基于SteamVR 2.0)

文章目录

  • 本文是个人在使用Unity开发VR过程中的一些记录,主要用于个人开发记录,文章中摘抄了大量其它博主原创文章,在参考链接中进行了标注
  • 本文内容较乱,将就着看吧QAQ

1.参考链接

2.SteamVR 插件

📕前言

输入系统是 VR 开发中非常重要的一部分。我们通常需要获取 VR 手柄上某个按键的输入,然后将其作用到应用中,比如按下手柄的 Grip 键进行抓取,就需要在检测到“按下手柄 Grip 键”的输入操作时,执行抓取的行为。

SteamVR 插件是 Valve 提供给 Unity 开发者的用于开发 PCVR (头显与电脑串流的形式)的插件。随着越来越多 VR 设备的推出,开发者往往会面临一个难题,就是将自己开发的应用适配到不同的设备上,但是不同设备的手柄可能会有不同的按键,比如 Meta Quest 手柄上有摇杆,但是 Htc Vive 手柄上只有个圆形的触控板,作用和 Quest 手柄的摇杆是一样的。

Quest 手柄:

在这里插入图片描述

Htc Vive 手柄(下图的 2 是触摸板):

在这里插入图片描述

于是为了将项目适配到其他厂商的设备,开发者需要修改输入按键的代码以适配新的设备,比如开发者原先的项目是运行在 Htc Vive 上面的,其中有一个“通过触摸 Vive 手柄的触摸板,来控制人物移动”的功能。现在项目需要适配 Quest ,那么代码需要修改成“通过推动 Quest 手柄的摇杆,来控制人物移动”。其他的按键也是类似的道理,即使在游戏中实现的功能是一样的,但是因为设备的不同,开发者需要修改代码适配相应的按键。

因此,SteamVR 2.x 版本(2.0 以上的版本,笔者写这篇文章的时候已经更新到了 2.7.3 版本)推出了全新的输入系统 SteamVR Input。它是基于动作 Action 来开发,将输入设备和动作逻辑互相分离,通过配置映射来处理输入信息。也就是说,输入系统相当于程序之外的一个配置文件,我们可以在输入系统中将输入按键与动作进行绑定,这和 Unity 新输入系统 Input System 是类似的。使用这种方式的好处是:

  1. 我们在代码中关心的是定义的动作是否触发,以及触发后需要执行什么样的方法,而不用关心是否监听到设备的输入,因为设备的输入会由输入系统自动监听。比如我们在输入系统配置文件中定义一个 Grab 动作,将它与 “按下 Htc Vive 手柄的 Grip 键” 和 “按下 Quest 手柄的 Grip 键”这两个输入操作进行绑定。当我们检测玩家是否抓取某个物体的时候,不是在代码中检测 Htc Vive 手柄的 Grip 键或 Quest 手柄的 Grip 键是否被按下,而是检测 Grab 动作是否为 true,因为检测设备输入交给了 SteamVR 输入系统本身。如果检测为 true,则执行抓取相关的代码。
  2. 以后我如果想把需求从“按下手柄 Grip 键进行抓取”改为“按下手柄 Trigger 键进行抓取”,那么我只需要在配置文件里将“按下手柄 Trigger 键”的输入操作与 Grab 动作绑定,形成映射关系,然后删除 Grab 动作与“按下手柄 Grip 键”的绑定,而代码不需要做任何改变。因为在代码中我们检测的是 Grab 动作是否触发,而不是检测什么具体的按键被按下。我们在代码中关心的是动作的触发,在程序之外的配置文件中关心的是动作与什么样的输入绑定,而监听输入完全交给了输入系统本身,不用我们自己编写监听输入相关的代码,这使得输入设备和代码逻辑互相分离,大大提高了可拓展性。

用一张图来表示:

在这里插入图片描述

总结一下,开发人员不需要将输入视为某一特定设备的特定按键,而是在程序之外定义动作并与按键进行绑定,程序代码中关心的是“做出某个动作发生什么事情”,而不是“按下某个按键发生什么事情”。这样新的设备可以快速适配程序,无需更改代码,只需在输入系统配置文件中设置新设备的按键与动作的绑定关系。

目前这种基于动作而不是基于按键的输入系统会逐渐成为未来处理输入的主流,相比于直接在代码中监听输入设备,基于动作与输入相映射的方法可能会更复杂一点,因为我们除了要在代码中处理动作,还要额外创建一个配置文件将动作与输入操作进行绑定,但是它拥有移植方便、可拓展性高的优点,这适用于多设备、多平台的开发。


📕教程说明

我使用的设备是 Meta Quest 2,使用 Meta Quest 2 开发 SteamVR 的前提是将 Quest 与电脑进行串流。如果你也是用 Quest 开发 SteamVR,首先电脑上要装一个 Oculus 电脑客户端(如下图所示),在电脑上打开它后,将头显连接电脑,然后在头显里点击 Oculus Link 进行串流,然后再连接 SteamVR。

在这里插入图片描述

使用的 Unity 版本: 2021.3.5

使用的操作系统:Windows 11

SteamVR 版本:2.7.3


📕导入 SteamVR 插件

我们可以在 Unity Asset Store 里搜索 SteamVR,将其添加进自己的资源。

在这里插入图片描述

然后在 Unity 中打开 Window/Package Manager:

在这里插入图片描述

在 My Asset 中找到 SteamVR Plugin,点击 Import 将其导入到项目中。

在这里插入图片描述

导入后可能会跳出下图的弹窗,点击 OK

在这里插入图片描述

如果出现了下图所示的弹窗,我们需要点击 Accept All,它会帮我们初始化一些配置,需要注意的是开发 SteamVR 的 Color Space 推荐使用的是 Linear(项目默认是 Gamma)

在这里插入图片描述

点击以后,建议重启一下项目,然后打开 Edit/Project Settings/XR Plugin Manager,确保勾选的是 OpenVR Loader,这样才能运行程序才会与 SteamVR 连接:

在这里插入图片描述

在这里插入图片描述

确认完毕后,可以在 Project 窗口中,路径 Assets/SteamVR/InteractionSystem/Samples下,打开场景文件Interactions_Example,这是 SteamVR 官方提供的一个交互场景,供开发者学习参考。

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

初次导入 SteamVR 插件并运行程序时,SteamVR 会检测项目是否存在动作以及动作与按键的绑定配置,如果没有,会打开一个弹窗询问是否打开SteamVR Input 窗口,我们选 Yes 就可以了。

在这里插入图片描述


📕SteamVR Input 窗口

⭐action.json 文件

在打开 SteamVR Input 窗口的过程中,SteamVR 插件会检测项目中是否存在 actions.json 文件,该文件存储了项目中动作(Action)与动作集(Action Sets)的信息,可以理解为输入系统的配置文件中存储了许多动作集,每一个动作集记录了一些动作与输入的映射关系。如果没有 actions.json 文件,插件会建议使用默认提供的示例文件,我们点击 Yes:

在这里插入图片描述

点击 Yes 按钮后,根据官方文档对这一操作的解释:

If you select your Window menu you’ll see a new item here called SteamVR Input. Click on that and you’ll likely get a dialog explaining that you’re missing an actions JSON and asking if you’d like to use the default. Select Yes and it’ll copy the default actions.json file, as well as the related bindings files for a few popular controllers into the root of your project directory. This is where SteamVR will read them from when you go into Play Mode and where it’ll copy them from when you make a build.

插件会将示例文件 actions.json 以及一些当前主流控制器的按键绑定配置文件拷贝到项目中的 Assets/StreamingAssets/SteamVR 目录下(如下图所示),未来在程序运行时,也将从此文件夹中读取用户关于动作的配置信息。

在这里插入图片描述

我使用的是 Quest 开发 SteamVR,这些按键绑定配置文件中有一个叫 Bindings_oculus_touch 的文件就是对应 Quest 的输入。

在这里插入图片描述

⭐窗口面板

以上是点击 Yes 后插件会在背后做的事情,然后就会出现如下图所示的 SteamVR Input 窗口。之前有介绍过,action.json 文件存储了项目中动作与动作集的信息。这个时候,SteamVR 会读取 action.json 文件,在窗口顶部的 Action Sets 下列出记录的所有动作集(默认的有 defalut,platformer,buggy,mixedreality)。选择任一动作集,会在下方的 Actions 下列出这个动作集下的所有动作,我们可以在这个列表里添加或删除动作,In 的下方记录的是与输入有关的动作,Out 下方记录的是与输出有关的动作,比如 Haptic 动作与手柄的震动输出有关。选择任一动作,可以在窗口右侧的 Action Details 下看到这个动作配置的详细信息。

在这里插入图片描述

⭐SteamVR_Input 目录

第一次打开这个窗口时,或者以后对这个窗口进行了修改,我们需要点击窗口下方的 Save and generate,它首先会把窗口中的动作配置信息保存在 action.json 文件中,然后会创建或更新一些动作类,之后可以在开发过程中通过代码对具体的动作进行引用,这些类的脚本位于 Assets/SteamVR_Input 目录下,如下图所示:

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

比如我点开一个 SteamVR_Actions 脚本,里面包含了刚刚在 SteamVR Input 窗口看到的一些动作变量(如下图所示),这些变量的数据类型(如下图中的 SteamVR_Action_Boolean,SteamVR_Action_Pose 等)是 SteamVR 为不同种类的动作设置的,我会在下一小节进行讲解。

在这里插入图片描述

以上便是对 SteamVR Input 窗口的简要介绍,至于具体如何使用这个输入系统窗口,稍后我也会进行讲解。

如果你不小心关闭了这个窗口,可以点击 Window/SteamVR Input 重新打开:

在这里插入图片描述


📕SteamVR 动作的类型

SteamVR 将动作的类型分为 6 个输入类型(Boolean,Single,Vector2,Vector3,Pose,Skeleton)和 1 个 输出类型(Vibration)。

官方文档:https://valvesoftware.github.io/steamvr_unity_plugin/articles/SteamVR-Input.html

⭐Boolean

Boolean 动作只返回 true 和 false 两种结果。检测是否按下手柄上的某个按键就能用 Boolean 类型表示,因为只有“按下按键”和“没按下按键”两种情况。比如我想在按下手柄 Grip 键的时候触发抓取,那么就是一个 Boolean 类型的动作检测为 true 时,触发抓取的逻辑。在 Unity 中对应类为 SteamVR_Action_Boolean。

⭐Single

Single 动作能够返回一个范围在 0-1 之间的数值。比如获取 Grip 键按下的程度,没按 Grip 键的时候 Single 的值为 0,随着逐渐按下 Grip 键,值会慢慢增大,按到底的时候值为 1。在 Unity 中对应类为 SteamVR_Action_Single。(注:Single 类型在 SteamVR Input 窗口中显示为 Vector1)

⭐Vector2

Vector2 动作能够返回一个二维向量,由 2 个值组成(x 和 y)。Vector2 类型经常用于表示手柄摇杆或触摸板的位置。因为摇杆或触摸板是在一个圆形范围内运动,我们可以将其想象为 x-y 坐标系下的一个圆心在原点,半径为 1 的圆,摇杆或触摸板运动后的位置就能用一个二维向量来表示。比如手柄摇杆向前方推到底,就会得到一个(0,1)的二维向量。如果需要推动摇杆或者触摸板来控制人物移动,就需要用到 Vector2 类型的动作。在 Unity 中对应类为 SteamVR_Action_Vector2。

在这里插入图片描述

⭐Vector3

Vector3 动作能够返回一个三维向量。在 Unity 中对应类为 SteamVR_Action_Vector3。

⭐Pose

Pose 动作表示三维空间中的位置和旋转,一般用于跟踪 VR 手柄,比如虚拟的手部跟踪 VR 手柄的姿态,手柄的位置和旋转数据就会通过 Pose 动作传回程序,然后将数据赋予虚拟的手部,这样虚拟手部的位置和旋转就会和现实世界中的手柄相对应。在Unity中对应类为 SteamVR_Action_Pose。

⭐Skeleton

Skeleton 动作能够获取用户在持握手柄时的手指关节数据,通过返回数据,结合手部渲染模型,能够更加真实的呈现手部在虚拟世界的姿态。这个动作一般是要结合手部模型,比如 Knuckles 指虎手柄拥有手指追踪的功能,可以估算用户手指的位置,然后将数据传递给程序,程序将其对应解析到手部模型的骨骼上,这样虚拟的手部骨骼姿态就能模拟现实中的手。除此之外,SteamVR 也有给像 Vive 或者 Quest 手柄提供手指状态估算的功能,比如判断手指是否放在触摸板或摇杆上,滑动触摸板或转动摇杆时会模拟手指关节的弯曲。在 Unity 中对应类为 SteamVR_Action_Skeleton。

⭐Vibration

Vibration 就是震动,与前面几种类型不同,它是一种输出类型,用于触发手柄上的震动反馈。


📕动作和按键绑定窗口 Binding UI

回顾刚刚介绍的 SteamVR Input 窗口:

在这里插入图片描述

我们选中一个动作后,在 Action Details 下方可以设置动作的名字、类型等属性。但是如果仅有这个窗口,我们还不知道这个动作与手柄的什么按键进行了绑定。因此,在 SteamVR Input 窗口创建了一个动作之后,或者想要修改原有动作的按键绑定,我们需要点击上图中 SteamVR Input 窗口中的 Open binging UI 按钮。点击后会出现如下界面(需要注意的是头显与 SteamVR 连接后才会打开如下界面):

在这里插入图片描述

因为我使用的是 Quest 2,所以显示的是 Oculus touch 控制器和与控制器匹配的绑定。点击编辑可以对动作和按键的绑定进行修改,会打开下图所示的界面,之后我们就是在这个界面里设置动作和按键的绑定关系:

在这里插入图片描述

⭐动作绑定案例讲解

比如现在我把鼠标光标移至 grabgrip 的板块上(如下图所示),它就会指向 Oculus Touch 的握持(Grip)键(Oculus Touch 是 Oculus Rift 设备的手柄,Quest 系列手柄的按键设置目前和 Rift 设备的手柄是一样的)

在这里插入图片描述

GrabGrip 是 SteamVR Input 窗口中定义的一个动作,此时它在 Binding UI 窗口中的名字是 grabgrip(没有区分大小写,和动作的名字是一样的),那么这个动作绑定的就是手柄的 Grip 键。然后我们可以点击下图中的像铅笔一样的符号:

在这里插入图片描述

在这里插入图片描述

🔍按键点击样例

可以看到“点击”的右侧对应的是 grabgrip,这个“点击”是什么意思呢?我们可以将鼠标光标移至“点击”文字处:

在这里插入图片描述

因为这个按键绑定属于扳机键的模块。所以它的意思就是按下 Grip 键时触发 grabgrip 这个动作。如果我们点击“更多选项”,界面会发生一些变化:

在这里插入图片描述

这时候“点击”变成了“单击”,界面也提供了更多的选项。严格来说,应该是按下一次 Grip 键触发 grabgrip 这个动作。除此之外,还有双击、长按、按压、触摸的选项,大家之后可以根据具体的开发需求进行选择。

🔍“作为按键使用”和“作为扳机键使用”的区别

另外,在 grabgrip 动作下方的一个板块是 squeeze 动作(如下图所示):

在这里插入图片描述

它们的区别是 grabgrip 动作绑定的东西是作为按键使用,触发条件是单击;squeeze 动作绑定的东西是作为扳机键使用,触发条件是扣动。作为按键使用和作为扳机键使用的区别是什么呢?我们可以点击“持握键”右侧的“+”号,然后会跳出如下图所示的界面,之后如果我们要为手柄按键添加动作的绑定也是这样操作。

在这里插入图片描述

也就是说,我们需要为手柄的某个按键选择一种操作的类型,也就是如何去使用这个按键。作为扳机键和作为按键是最常用的选项,我们可以点击右侧的问号,然后会显示使用说明。

作为扳机键:

在这里插入图片描述

作为按键:

在这里插入图片描述

这里作为扳机键的意思是像扳机键一样使用,因为扳机键有个按下的程度,所以和“作为按键使用”相比,它能够返回一个 0-1 之间的值。可以看到持握键作为扳机键使用时,扣动触发 squeeze 动作(如下图所示):

在这里插入图片描述

也就是扣动 Grip 键的时候,会返回一个 0-1 之间的值,没按 Grip 键的时候,返回 0,扣动 Grip 键直至按到底的时候,值会逐渐增大到 1。所以“作为扳机键使用”经常和 Single 类型的动作绑定。而作为按键使用则无法设置扣动的操作,它没法返回一个值,表示按键按下的程度,但是它在点击上提供了如双击之类的更多操作。

总结来说,我们可以在 SteamVR Input 窗口添加、删除、修改动作的属性,或者添加、删除动作集,这个界面的所有操作都是和动作有关,而 Binding UI 界面是用来为手柄按键绑定对应的动作,并且可以选择使用按键的方式,比如单击,长按,获取按键按下的程度等。

⭐Localized String,Languages 与 Binding UI 的联系

之前在介绍 SteramVR Input 窗口的时候,在面板的 Action Details 下有一个 Languages 和 Localized String 属性还没有介绍(如下图所示)。

在这里插入图片描述

Localized String 是本地化字符串的意思,Languages 是 Steam 页面的语言,它们和动作和按键绑定窗口 Bindigng UI 有一定的联系。此时我们点击 Open binding UI,打开配置界面:

在这里插入图片描述

当你在看其他 Unity SteamVR 开发教程的时候,可能会发现上图中用红框标出的动作名字变成了 SteamVR Input 窗口中 Localized String 的名字。但也许你的界面会和我一样,这些动作的名字是 SteamVR Input 窗口中 Action Details 下的 Name 的名字(不区分大小写)

在这里插入图片描述

这是因为只有 Steam 页面的语言和 SteamVR Input 窗口中的 Lanuages 设置一样时,Binding UI 中的动作名字才会和 Localized String 一样。我的 Steam 页面语言为简体中文,但是 SteamVR Input 窗口中的 Lanuages 只有一个 en_US(英语),所以 Binding UI 中的动作名字不是 Localized String,而默认是 SteamVR Input 窗口中 Action Details 下的 Name 的名字(不区分大小写)

现在,我将 Steam 页面的语言改成英语,需要在 Steam 中点击左上角的"Steam”,点击“界面”,然后修改 Steam 客户端语言:

在这里插入图片描述

改成英语后,重新打开 Binding UI,可以看到动作的名字变成了 Localized String 的名字:

在这里插入图片描述

在这里插入图片描述

但是如果我就想在 Steam 语言为简体中文的情况下开发,需要怎么做才能让 Binding UI 的动作名字与 Localized String 一样呢?

可以看到 SteamVR Input 窗口中的 Languages 是可以添加的,en_US 表示英语,那么我们只需要添加简体中文的语言代码。全世界的语言代码可以参考这个网址:http://www.lingoes.net/en/translator/langcode.htm(需要把“-”换成“_”),简体中文的语言代码为 zh_CN,因此我们可以在 SteamVR Input 窗口中的 Languages 下添加一个 zh_CN 语言代码,以 InteractUI 动作为例:

在这里插入图片描述

添加后记得点击 Save and generate,然后点击 Open binding UI:

在这里插入图片描述

可以看到 InteractUI 动作在 Binding UI 中的名字已经和语言代码是 zh_CN 下的 Localized String 的名字一样了。如果你打开 Binding UI 发现名字没有改变,可以尝试关闭 SteamVR Input 窗口,然后重新设置、保存,再打开 Binding UI。

⭐镜像模式

在这里插入图片描述

Binding UI窗口默认是开启了镜像模式,这样只要配置了一边手柄,另一边手柄也会自动绑定。如果你想左右手柄绑定不同的动作,可以取消勾选镜像模式。


📕用代码获取动作

当我们配置好了动作,以及动作与按键的绑定关系后,我们需要在代码中引用动作。

首先我们简单地搭建下场景,在 Unity 中新建一个场景后,删除场景中的 Main Camera,添加一个平面,然后在项目的 Assets/SteamVR/Prefabs 文件夹中找到 [CameraRig] 预制体,将它拖入场景。这个预制体相当于 VR 中的玩家自己,它拥有头部摄像机,相当于虚拟世界中的眼睛,并且能够追踪手柄的姿态,运行程序后手部会渲染出当前使用的设备的手柄模型。

然后我们随便创建一个脚本添加到一个空物体上,待会儿我们就用这个脚本来演示:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

要想引用 SteamVR 中设置的动作,先要在脚本中引用 Valve.VR 命名空间

using Valve.VR;

⭐获取 Boolean 类型的动作

现在以“按下手柄 Grip 键”,也就是 Boolean 类型的动作为例,讲解如何用代码获取动作,以及判断动作是否触发。

🔍在 Inspector 面板中赋值

之前介绍过,Boolean 动作在 Unity 中对应的类是 SteamVR_Action_Boolean,所以我们可以在脚本中声明一个 SteamVR_Action_Boolean 类型的公共变量:

public SteamVR_Action_Boolean booleanAction;

这样 Unity 编辑器中的 Inspector 面板就会显示这个变量:

在这里插入图片描述

我们可以在面板中选择一个动作赋予这个变量,根据 Binding UI 中的动作按键绑定关系,按下手柄 Grip 键对应的是 \actions\default\in\GrabGrip,意思是名为“defalut”的动作集下,输入类型(in)动作下的 GrabGrip 动作。现在,我们的脚本就已经连接了 SteamVR 的输入系统,之后我们就需要判断这个动作是否发生,以及编写动作发生后执行的事情。

在这里插入图片描述

🔍判断动作发生方法一:添加事件

我们可以这样编写脚本:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Valve.VR;

public class InputTest : MonoBehaviour
{
    public SteamVR_Action_Boolean booleanAction;
    void Start()
    {
        booleanAction.onStateDown += OnStateDown;
    }
    private void OnDestroy()
    {
        booleanAction.onStateDown -= OnStateDown;
    }
    private void OnStateDown(SteamVR_Action_Boolean fromAction, SteamVR_Input_Sources fromSource)
    {
        print($"{fromAction.activeDevice},{fromSource}");
    }
    void Update()
    {
        
    }
}

SteamVR_Action_Boolean 类中提供了一些事件,onStateDown 是在动作由 false 变为 true 的触发,在我们的场景下就是手柄 Grip 键由没按下变成按下的状态时触发。

其他事件的定义可以参考源码:

在这里插入图片描述

onChange //This event fires whenever a state changes from false to true or true to false
onUpdate //This event fires whenever the action is updated
onState //This event fires whenever the boolean action is true and gets updated
onStateDown //This event fires whenever the state of the boolean action has changed from false to true in the most recent update
onStateUp //This event fires whenever the state of the boolean action has changed from true to false in the most recent update
onActiveChange //Event fires when the active state (ActionSet active and binding active) changes
onActiveBindingChange //Event fires when the bound state of the binding changes

然后刚刚使用的 onStateDown 事件绑定的方法需要有 2 个参数,第一个是 SteamVR_Action_Boolean 类型,第二个是 SteamVR_Input_Sources 类型:

private void OnStateDown(SteamVR_Action_Boolean fromAction, SteamVR_Input_Sources fromSource)
{
     print($"{fromAction.activeDevice},{fromSource}");
}

这个方法会在 GrabGrip 动作发生,也就是按下手柄 Grip 键时触发。如果我们运行程序,分别按下右手柄和左手柄的 Grip 键,就会在 Unity 控制台看到输出的文字:

在这里插入图片描述

fromAction.activeDevice 是个 SteamVR_Input_Sources 的枚举,它能够获取当前哪只手正在操作。fromSource 也是个 SteamVR_Input_Sources 的枚举,我们可以打开源码:

namespace Valve.VR
{
    public enum SteamVR_Input_Sources
    {
        [Description("/unrestricted")] //todo: check to see if this gets exported: k_ulInvalidInputHandle
        Any,

        [Description("/user/hand/left")]
        LeftHand,

        [Description("/user/hand/right")]
        RightHand,

        [Description("/user/foot/left")]
        LeftFoot,

        [Description("/user/foot/right")]
        RightFoot,

        [Description("/user/shoulder/left")]
        LeftShoulder,

        [Description("/user/shoulder/right")]
        RightShoulder,

        [Description("/user/waist")]
        Waist,

        [Description("/user/chest")]
        Chest,

        [Description("/user/head")]
        Head,

        [Description("/user/gamepad")]
        Gamepad,

        [Description("/user/camera")]
        Camera,

        [Description("/user/keyboard")]
        Keyboard,

        [Description("/user/treadmill")]
        Treadmill,
    }
}

但是无论是按下左 Grip 键还是右 Grip 键,fromSource 返回的都是 Any。这是因为我们没有给定义的 SteamVR_Action_Boolean 类的变量指定输入源,如果我们这样操作:

booleanAction[SteamVR_Input_Sources.LeftHand].onStateDown += OnStateDown;

这时候 fromSource 就会返回 LeftHand。

如果你是选用为动作添加事件的方法,一定要记得在合适的地方移除事件,比如我就是在 OnDestroy 脚本销毁的时候移除事件:

private void OnDestroy()
{
    booleanAction.onStateDown -= OnStateDown;
}
🔍判断动作发生方法二:条件语句

我们可以这样操作,在 Update 方法添加条件判断语句检测动作是否发生:

void Update()
{
    if (booleanAction.stateDown)
    {
        print("手柄grip键按下");
    }
}

SteamVR_Action_Boolean 类提供了几个公共的 bool 变量用于判断动作的发生:

/// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> True when the boolean action is true</summary>
public bool state { get { return sourceMap[SteamVR_Input_Sources.Any].state; } }

/// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> True when the boolean action is true and the last state was false</summary>
public bool stateDown { get { return sourceMap[SteamVR_Input_Sources.Any].stateDown; } }

/// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> True when the boolean action is false and the last state was true</summary>
public bool stateUp { get { return sourceMap[SteamVR_Input_Sources.Any].stateUp; } }


/// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> (previous update) True when the boolean action is true</summary>
public bool lastState { get { return sourceMap[SteamVR_Input_Sources.Any].lastState; } }

/// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> (previous update) True when the boolean action is true and the last state was false</summary>
public bool lastStateDown { get { return sourceMap[SteamVR_Input_Sources.Any].lastStateDown; } }

/// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> (previous update) True when the boolean action is false and the last state was true</summary>
public bool lastStateUp { get { return sourceMap[SteamVR_Input_Sources.Any].lastStateUp; } }

stateDown 变量就是在动作由 false 变为 true 时,返回 true。
这时候无论是左手柄还是右手柄都会触发,如果我们想要限定触发的手柄,可以这么做:

void Update()
{
    if (booleanAction.GetStateDown(SteamVR_Input_Sources.LeftHand))
    {
        print("左手柄grip键按下");
    }
}

或者

void Update()
{
    if (booleanAction.stateDown)
    {
        if(booleanAction.activeDevice == SteamVR_Input_Sources.LeftHand)
        {
            print("左手柄grip键按下");
        }
    }
}
🔍静态访问

除了在 Inspector 面板中对动作进行赋值,我们也可以直接在代码中静态访问动作类:

SteamVR_Actions.default_GrabGrip.onStateDown += OnStateDown;
//或者
SteamVR_Actions._default.GrabGrip.onStateDown += OnStateDown;

这些动作类是之前在 SteamVR Input 窗口点击 Save and generate 后系统自动为我们创建的。SteamVR_Actions.default_GrabGrip 就是一个 SteamVR_Action_Boolean 类型。

⭐获取 Single 类型动作的值

同样我们可以通过在 Inspector 面板中赋值或者静态访问引用动作。我这里都选用 Inspector 面板中赋值的方式。

public SteamVR_Action_Single singleAction;

在这里插入图片描述
我们可以选择 Squeeze 动作:

在这里插入图片描述

扣动 Grip 键能获取一个 0-1 之间的值。

🔍通过事件获取
singleAction.onAxis += OnSingleAction;

private void OnSingleAction(SteamVR_Action_Single fromAction, SteamVR_Input_Sources fromSource, float newAxis, float newDelta)
{
    print($"newAxis:{newAxis},newDelta:{newDelta}");
}

输出结果:
在这里插入图片描述

newAxis 就是根据按下的程度返回的值,newDelta 是相较于上一个值的差值。因此我们可以通过 newAxis 获取 Single 动作的值。

除了 onAxis 事件,还有其他种类的事件,大家可以参考源码的解释:

 /// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> This event fires whenever the axis changes by more than the specified changeTolerance</summary>
public event ChangeHandler onChange{ add { sourceMap[SteamVR_Input_Sources.Any].onChange += value; } remove { sourceMap[SteamVR_Input_Sources.Any].onChange -= value; } }

/// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> This event fires whenever the action is updated</summary>
public event UpdateHandler onUpdate{ add { sourceMap[SteamVR_Input_Sources.Any].onUpdate += value; } remove { sourceMap[SteamVR_Input_Sources.Any].onUpdate -= value; } }

/// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> This event will fire whenever the float value of the action is non-zero</summary>
public event AxisHandler onAxis{ add { sourceMap[SteamVR_Input_Sources.Any].onAxis += value; } remove { sourceMap[SteamVR_Input_Sources.Any].onAxis -= value; } }

/// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> This event fires when the active state (ActionSet active and binding active) changes</summary>
public event ActiveChangeHandler onActiveChange{ add { sourceMap[SteamVR_Input_Sources.Any].onActiveChange += value; } remove { sourceMap[SteamVR_Input_Sources.Any].onActiveChange -= value; } }

/// <summary><strong>[Shortcut to: SteamVR_Input_Sources.Any]</strong> This event fires when the active state of the binding changes</summary>
public event ActiveChangeHandler onActiveBindingChange{ add { sourceMap[SteamVR_Input_Sources.Any].onActiveBindingChange += value; } remove { sourceMap[SteamVR_Input_Sources.Any].onActiveBindingChange -= value; } }
🔍通过变量获取
singleAction.axis

如果想要限定左右手柄,可以这么操作:

singleAction[SteamVR_Input_Sources.LeftHand].axis
singleAction[SteamVR_Input_Sources.RightHand].axis
🔍通过方法获取
singleAction.GetAxis(SteamVR_Input_Sources.LeftHand)

⭐获取 Vector2 类型动作的值

public SteamVR_Action_Vector2 vector2Action;

默认的 Vector2 类型有两个:

在这里插入图片描述

但是经测试发现,无法获取它们的值。因为这两个动作所属的动作集不是 default 默认动作集,所以一开始默认它们不是被激活的。至于如何激活其他动作集,我会在稍后进行讲解。

🔍自定义动作

因此,我们可以自己在 defalut 动作集下创建一个 Vector2 类型的动作用于测试。首先打开 SteamVR Input 窗口,在 default 动作集下添加一个动作,把类型设为 Vector2:

在这里插入图片描述

然后点击 Save and generate,如果界面上出现了一个 compiling 代表成功。如果失败了大家可以重新打开 SteamVR Input 窗口,再试一次。

保存成功后点击 Open binding UI,进行按键绑定。

在这里插入图片描述

我们在 JoyStick 下(如果是 Htc Vive 应该是 Touchpad)添加一个绑定,作为摇杆使用,并且将“位置”设置为 joystick 动作。“位置”表示在触摸板上触摸的位置或者将摇杆推至的位置。因为此时处于镜像模式,所以我们新添加的动作就成功地和左右手柄的摇杆绑定好了。

然后在 Inspector 面板中对变量赋值:

在这里插入图片描述

现在就能够通过代码获取 Vector2 类型动作的值,获取方式和获取 Single 类型动作的值是一样的。

🔍通过事件获取
vector2Action.onAxis += OnVector2Action;

private void OnVector2Action(SteamVR_Action_Vector2 fromAction, SteamVR_Input_Sources fromSource, Vector2 axis, Vector2 delta)
{
    print($"newAxis:{axis},newDelta:{delta}");
}
🔍通过变量获取
vector2Action.axis
🔍通过方法获取
vector2Action.GetAxis(SteamVR_Input_Sources.LeftHand)

⭐获取 Pose 类型动作的值

public SteamVR_Action_Pose poseAction;

在这里插入图片描述

因为 Pose 动作对应的是手部的姿态,所以最常用的用法是获取手部的本地坐标和本地旋转角度

poseAction.localPosition
poseAction.localRotation

如果想要限定手柄,可以这么做:

poseAction[SteamVR_Input_Sources.LeftHand].localPosition

或者用方法获取:

poseAction.GetLocalPosition(SteamVR_Input_Sources.LeftHand);

⭐手柄震动

SteamVR_Actions._default.Haptic.Execute(float secondsFromNow, float durationSeconds, float frequency, float amplitude, SteamVR_Input_Sources inputSource)

参数解释

secondsFromNow:从当前时间到执行震动动作之间需要多长的时间。也就是开始震动前需要多久的准备时间,也可以理解为震动的延迟时间。
durationSeconds:震动持续时间
frequency:震动马达多久反弹一次(范围是0-320hz)
amplitude:震动强度(范围0-1)
inputSource:输入源,一般指左右手柄

举个例子,我想在按下左手柄 Grip 键时震动左手柄,延续刚才的代码,可以这么做:

if (booleanAction.GetStateDown(SteamVR_Input_Sources.LeftHand))
{
    print("左手柄grip键按下");
    SteamVR_Actions._default.Haptic.Execute(0, 0.5f, 100, 0.5f, SteamVR_Input_Sources.LeftHand);
}

震动的参数可以自己调整。


📕测试动作窗口

点击 Window/SteamVR Input Live View 可以打开测试动作窗口:
在这里插入图片描述

运行程序后可以观察窗口变化:

在这里插入图片描述

📕SteamVR 内置的动作相关脚本

SteamVR 为我们提供了几个动作相关脚本:
SteamVR_Behaviour_Boolean, SteamVR_Behaviour_Single, SteamVR_Behaviour_Vector2, SteamVR_Behaviour_Vector3, SteamVR_Behaviour_Pose, and SteamVR_Behaviour_Skeleton

我们可以把它们挂载到游戏物体上:

在这里插入图片描述

然后在面板上设置参数,原理和刚刚介绍的用代码获取动作是一样的,当然,我们也可以在自己写的脚本中去获取动作,判断动作是否发生。

📕激活/停用动作集

使用脚本 SteamVR_ActivateActionSetOnLoad可以在场景中自动激活和停用指定的动作集。我们可以将它挂载到游戏物体上:

在这里插入图片描述

我们可以看看它的代码:

//======= Copyright (c) Valve Corporation, All rights reserved. ===============

using UnityEngine;
using System.Collections;

namespace Valve.VR
{
    /// <summary>
    /// Automatically activates an action set on Start() and deactivates the set on OnDestroy(). Optionally deactivating all other sets as well.
    /// </summary>
    public class SteamVR_ActivateActionSetOnLoad : MonoBehaviour
    {
        public SteamVR_ActionSet actionSet = SteamVR_Input.GetActionSet("default");

        public SteamVR_Input_Sources forSources = SteamVR_Input_Sources.Any;

        public bool disableAllOtherActionSets = false;

        public bool activateOnStart = true;
        public bool deactivateOnDestroy = true;

        public int initialPriority = 0;

        private void Start()
        {
            if (actionSet != null && activateOnStart)
            {
                //Debug.Log(string.Format("[SteamVR] Activating {0} action set.", actionSet.fullPath));
                actionSet.Activate(forSources, initialPriority, disableAllOtherActionSets);
            }
        }

        private void OnDestroy()
        {
            if (actionSet != null && deactivateOnDestroy)
            {
                //Debug.Log(string.Format("[SteamVR] Deactivating {0} action set.", actionSet.fullPath));
                actionSet.Deactivate(forSources);
            }
        }
    }
}

因此,之后我们也可以在自己的脚本中模仿这个代码激活或停用指定动作集。

3.SteamVR 实现基本功能(瞬移,抓取物品,射线点击,UI交互等)

3.1 基础设置

把SteamVR的Player预制件拖到一个空场景,删掉场景内原本的相机
在这里插入图片描述

3.2 瞬移

  1. 新建一个Plane,当做地板
  2. 找到SteamVR的人物瞬移控制器 Teleporting ,把它拖到场景里
    在这里插入图片描述

3.2.1 范围移动

在可以移动的区域,也就是碰撞器上,挂TeleportArea脚本

  • 这个脚本会自动修改你的材质球
  • locked:该区域是否可以移动
  • markerActive:区域跟随按键显示隐藏
    在这里插入图片描述
    因为这个脚本会更改材质球的属性,所以我们不能直接给地板挂这个脚本
  • 新建一个Plane,给它改名字TeleportArea
  • 给TeleportArea对象挂TeleportArea脚本
  • 将TeleportArea对象调整到合适位置和合适大小
  • 现在运行项目,已经可以通过Teleport瞬移到挂载TeleportArea脚本的区域了
  • 如果我们不想要TeleportArea生成的边框显示出来,那么就把MeshRenderer关掉就好了

3.2.2 定点移动

找到TeleportPoint预制件,将它拖到场景里
在这里插入图片描述
在这里插入图片描述
参数解释:

  • 前两项同上述一致
  • TeleportType:移动类型(MoveToLocation-移动到节点位置,SwitchToNewScene-前往设定场景)
  • Title: 提示(会在传送点上生成提示文字)
  • SwitchToScene:场景名字(TeleportType选择切换场景的话,就会前往这里设定的场景)
  • titleVisibleColor:提示文字默认颜色
  • titleHighlightedColor:传送点被选中时,文字高亮的颜色
  • titleLockedColor:传送点不可用,文字颜色
  • playerSpawnPoint:将当前传送点设置为玩家初始默认点(游戏启动后,会将Player位置重置到此处)

3.3 模型交互

3.3.1 Interactable参数介绍

请添加图片描述
  如果要实现触摸,拾取,就绕不开Interactable组件,Hand的所有交互,都是依赖Interactable组件,所以我们首先必须要清楚这个组件上的所有参数代表的内容,才能开发出符合需求的项目

  • Active Action Set On Attach:在拾取物体时激活的动作集(就是SteamVR Input设置的Actions)
  • Hide Hand On Attach:拾取物体时是否隐藏手
  • Hide Skileton On Attach:拾取物体时是否隐藏骨骼
  • Hide Controller On Attach:拾取物体时是否隐藏控制器
  • Hand Animation On PickUp:拾取物体时手部动画(与SteamVR_Skeleton_Poser配合使用)
  • Set Range Of Motion On PickUp:设置手部拾取的运动状态
  • Use Hand Object Attachment Point:指定使用手的附着物体的位置点,还是直接使用手的位置作为挂载点
  • Attach Ease In:开启缓动
  • Snap Attach Ease In Time:缓动时间
  • Snap Attach Ease In Completed:发送缓动完成事件
  • Hand Follow Transform:手部跟随对象
  • Highlight On Hover:悬停在游戏物体上时,游戏物体高亮(包含所有子物体)
  • Hide Hightlight:不参与高亮的游戏物体列表
  • Hover Priority:层级,数值越大,Hover越优先(当多个物品堆叠在一起时,会优先选中层级高的对象)

3.3.2 Interactable函数介绍

打开脚本之后我们能看到的交互事件,有下面这四个

  • OnHandHoverBegin:触摸到
  • OnHandHoverEnd:触摸离开
  • OnAttachedToHand:抓取到
  • OnDetachedFromHand:松手后
    如果要在属性面板上,对不同的回调,添加方法,可以添加InteractableHoverEvents组件
    在这里插入图片描述

因为Hand和Interactable之间的信息传递是靠SendMessage所以还隐藏着别的事件

  • HandHoverUpdate:持续触摸
  • HandAttachedUpdate:持续抓取

这些函数会在后面的拾取等交互中用到,了解这些,在后面的开发中,思路和逻辑会更加通顺

3.3.3 拾取物品

SteamVR插件内置了基于Interactable组件,拾取的功能
创建一个被拾取的道具

  • 新建一个Cube,Cube需要有Collider碰撞器
  • 给Cube添加Interactable交互组件
  • 添加Throwable拾取组件和RIgidbody刚体组件

现在创建的Cube已经具有被拾取的功能了
在这里插入图片描述
Throwable参数详解:

  • attachmentFlags:
    这个参数类型是一个多选枚举AttachmentFlags,用来控制不同的抓取效果
public enum AttachmentFlags
{
      SnapOnAttach = 1 << 0, // 对象应该吸附到手上指定的连接点的位置。  
      DetachOthers = 1 << 1, // 如果抓到物体的这只手还抓着别的对象,那么旧的对象会被抛开
      DetachFromOtherHand = 1 << 2, // 如果这个物体在别的手上,会替换到现在抓取它的这只手
      ParentToHand = 1 << 3, // 该对象被抓取后,会变成手的子对象
      VelocityMovement = 1 << 4, // 物体将尝试移动以匹配手的位置和旋转。  
      TurnOnKinematic = 1 << 5, // 这个物体不会对外部物理反应。打开刚体的Kinemati
      TurnOffGravity = 1 << 6, // 这个物体不会对外部物理反应。关闭重力
      AllowSidegrade = 1 << 7, //该物体能够从捏抓器切换到抓握器。 降低了投掷成功的可能性,同时也降低了意外掉落的可能性  
};
  • attachmentOffset:
    在抓取后对象相对手的位置和角度,会根据这个Transform信息偏移,就是说,你可以在手的控制器下,自己创建一个,想要被依附的位置
  • catchingSpeedThreshold:
    谷歌机翻了一下官方介绍,由于按住扳机而不是按下扳机,该物体必须以多快的速度移动才能附着?-1:禁用
    看代码,如果cube的刚体速度,大于这个计算出来的值,才可以被抓取
  • releaseVelocityStyle:
    松手释放后,物体的速度角度变化,也是一个枚举ReleaseStyle
    ShortEstimation和AdvancedEstimation需要添加VelocityEstimator组件
public enum ReleaseStyle
{
        NoChange,//无变化
        GetFromHand,//跟随手
        ShortEstimation,//短计算
        AdvancedEstimation,//长计算
}
  • releaseVelocityTimeOffset:
    releaseVelocityStyle使用FromHand选项释放对象时使用的时间偏移量
  • scaleReleaseVelocity:释放速度增量,如果你想把对象扔的远一点,就调大点
  • scaleReleaseVelocityThreshold:释放速度增量阈值
  • scaleReleaseVelocityCurve:释放速度增量曲线
  • restoreOriginalParent:对象松手后,返回原父物体下

Throwable监听事件:

  • OnPickUp-当拾取到
  • OnDetachFromHand-当放下时
  • OnHeldUpdate-当抓着时(每帧调用)
    在这里插入图片描述
    VelocityEstimator组件:
    刚刚在介绍 Throwable时,发现releaseVelocityStyle松手时速度变化用到了两个值ShortEstimation和AdvancedEstimation
    这个组件就是用来根据位置的变化估计物体的速度,在松手的时候,达到更仿真的效果
    和Throwable搭配起来使用的
    至此,一个可以被拾取的Cube,所需要的所有组件,和组件属性,都可以跟随我们的需求设定了
    在这里插入图片描述

3.4 射线交互

参考:

3.5 UI交互

SteamVR内置的UI交互组件,需要UIElement、Interactable、Collider
交互的时候需要手柄去碰到这个按钮,而且不支持Slider之类的需要拖动的UI,我们了解一下就好了

3.6 UGUI射线交互系统

4.HTC.UnityPlugin 插件

Vive Input Utility (VIU) 是由 HTC 提供的一款 Unity 插件,专门用于优化 Vive 和其他 VR 设备(例如 Vive、Oculus、Windows MR)在 Unity 中的输入管理。它提供了一系列功能,方便开发者轻松地集成和管理 VR 输入和交互,从而减少开发中的兼容性和输入问题。
HTC.UnityPlugi的优势:相对于Steamvr来说,方便获取手柄按键、方便与UGUI进行交互

4.1 安装 Vive Input Utility 插件

  • 方法 1:通过 Unity Asset Store 安装。
    打开 Unity 编辑器,点击菜单栏的 Window > Asset Store(或者直接在浏览器打开 Asset Store 网站)。
    搜索 Vive Input Utility 并将插件添加到项目中。
  • 方法 2:从 GitHub 下载。
    前往 Vive Input Utility GitHub 仓库页面,下载最新版本的 VIU。
    将下载的插件解压,并将文件夹拖到你的 Unity 项目 Assets 文件夹中。

PS:HTC.UnityPlugin自带示例场景位置:HTC.UnityPlugin - ViveInputUtility - Examples - 下级目录0至7文件夹内,每个文件夹含一个示例

4.2 VIU自带预制体介绍

HTC.UnityPlugin自带 预 制 体 位置:HTC.UnityPlugin - ViveInputUtility - Prefabs
在这里插入图片描述

  • ViveCameraRig:就像玩家本身,包括眼睛和手,查看VR场景必备
  • VivePointers:就是手柄的射线,射线检测必备
  • ViveColliders:与3d物体交互必备(示例场景5)
  • ViveRig:综合了ViveCameraRig,VivePointers,ViveColliders功能,并且还提供了激活按钮选项.(示例场景6)
  • ViveCurvePointers:预制体抛物线(示例场景10)
  • VivePointers:预制体直线

4.3 基本交互

查看链接:
https://www.cnblogs.com/kao-la-bao-bei/p/12777659.html
https://blog.csdn.net/u010645062/article/details/126423571

4.4 获取按键输入

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 引入该命名空间
using HTC.UnityPlugin.Vive;

public class TestHandKey : MonoBehaviour
{
    // 给按键添加事件(以 Menu 为例)
    private void Awake()
    {
#if false

        // 按住不放:右手的 Menu键 调用方法一
        ViveInput.AddPress(HandRole.RightHand, ControllerButton.Menu, One);
        // 按下:右手的 Menu键 调用方法二
        ViveInput.AddPressDown(HandRole.RightHand, ControllerButton.Menu, Two);
        // 点击:右手的 Menu键 调用方法三(点击:按下之后立刻抬起才会响应,按住不放 一段时间后再放开,则不会调用)
        ViveInput.AddClick(HandRole.RightHand, ControllerButton.Menu, Three);

#endif
    }

    // 方法一
    private void One()
    {
        print("调用了 方法一");
    }

    // 方法二
    private void Two()
    {
        print("调用了 方法二");
    }

    // 方法三
    private void Three()
    {
        print("调用了 方法三");
    }

    // 测试按键的按下、按住、抬起
    private void Update()
    {
#if false

        // 打印 Trigger 的返回值
        print("Trigger的返回值为:" + ViveInput.GetTriggerValue(HandRole.RightHand, false));
        // 打印 Pad 的坐标值
        print("Pad的坐标:" + ViveInput.GetPadAxis(HandRole.RightHand, false));

#endif

#if true

        // Menu
        if (ViveInput.GetPressDown(HandRole.RightHand, ControllerButton.Menu))
        {
            print("右手 - Menu - 按下");
        }
        else if (ViveInput.GetPress(HandRole.RightHand, ControllerButton.Menu))
        {
            print("右手 - Menu - 按住不放");
        }
        else if (ViveInput.GetPressUp(HandRole.RightHand, ControllerButton.Menu))
        {
            print("右手 - Menu - 抬起");
        }

        // Trigger
        if (ViveInput.GetPressDown(HandRole.RightHand, ControllerButton.Trigger))
        {
            print("右手 - Trigger - 按下");
        }
        else if (ViveInput.GetPress(HandRole.RightHand, ControllerButton.Trigger))
        {
            print("右手 - Trigger - 按住不放");
        }
        else if (ViveInput.GetPressUp(HandRole.RightHand, ControllerButton.Trigger))
        {
            print("右手 - Trigger - 抬起");
        }

        // pad(是否触摸到pad,通过判断GetPadAxis的返回时是否是(0,0)来判断)
        if (ViveInput.GetPressDown(HandRole.RightHand, ControllerButton.Pad))
        {
            print("右手 - Pad - 按下");
        }
        else if (ViveInput.GetPress(HandRole.RightHand, ControllerButton.Pad))
        {
            print("右手 - Pad - 按住不放");
        }
        else if (ViveInput.GetPressUp(HandRole.RightHand, ControllerButton.Pad))
        {
            print("右手 - Pad - 抬起");
        }

        // Grip
        if (ViveInput.GetPressDown(HandRole.RightHand, ControllerButton.Grip))
        {
            print("右手 - Grip - 按下");
        }
        if (ViveInput.GetPress(HandRole.RightHand, ControllerButton.Grip))
        {
            print("右手 - Grip - 按住不放");
        }
        if (ViveInput.GetPressUp(HandRole.RightHand, ControllerButton.Grip))
        {
            print("右手 - Grip - 抬起");
        }

#endif

#if false

        // Trigger 轻按、重按、按到底
        if (ViveInput.GetPressDown(HandRole.RightHand, ControllerButton.HairTrigger))
        {
            print("Trigger - 轻按");
        }
        else if (ViveInput.GetPressDown(HandRole.RightHand, ControllerButton.Trigger))
        {
            print("Trigger - 重按");
        }
        else if (ViveInput.GetPressDown(HandRole.RightHand, ControllerButton.FullTrigger))
        {
            print("Trigger - 按到底");
        }

#endif

#if false

        // GetPadTouchAxis:触摸 就会返回坐标值
        print(ViveInput.GetPadTouchAxis(HandRole.RightHand));
        // 从左向右滑,横坐标为正;     从右向左滑,横坐标为负;    从下向上滑,纵坐标为正;    从上向下滑,纵坐标为负
        print(ViveInput.GetPadTouchDelta(HandRole.RightHand));
        // GetPadTouchVector:以开始触摸点为(0,0),若触摸点为pad的最下方,当手指滑到最上方时坐标值为(0,2)
        print(ViveInput.GetPadTouchVector(HandRole.RightHand));
        // GetPadTouchAxis 为触摸反馈;GetPadPressAxis 为按住反馈
        print(ViveInput.GetPadPressAxis(HandRole.RightHand));
        print(ViveInput.GetPadPressDelta(HandRole.RightHand));
        print(ViveInput.GetPadPressVector(HandRole.RightHand));

#endif
    }
}

4.5 物体与手交互事件

摸,按,点,拾,扔,交互条件:物体必须加碰撞体,刚体可加可不加.

// 实现 IColliderEventHoverEnterHandler 接口
// 触摸
IColliderEventHoverEnterHandler
IColliderEventHoverExitHandler 
IColliderEventLastHoverEnterHandler 
IColliderEventLastHoverExitHandler 
// 按压 trigger 键按下
IColliderEventPressDownHandler 
IColliderEventPressUpHandler 
IColliderEventPressEnterHandler 
IColliderEventPressExitHandler 
// 点击
IColliderEventClickHandler 
// 拾取
IColliderEventDragStartHandler 
IColliderEventDragFixedUpdateHandler 
IColliderEventDragUpdateHandler 
IColliderEventDragEndHandler 
// 扔掉
IColliderEventDropHandler 

除了BasicGrabbable基础抓取的脚本外,vive还提供了另一种抓取脚本StickyGrabbable.(在场景5中体验)
StickyGrabbable效果是手松开,物体也不会放,再按一次按钮才会放.
StickyGrabbable多了个ToggleToRelease属性,就是来控制这个效果的.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值