文章目录
- [1 手势抓取方式](#1 手势抓取方式)
-
- [1.1 Hand Grab](#1.1 Hand Grab)
- [1.2 Touch Hand Grab](#1.2 Touch Hand Grab)
- [1.3 Distance Hand Grab](#1.3 Distance Hand Grab)
- [2 HandGrabExamples 示例场景](#2 HandGrabExamples 示例场景)
-
- [2.1 Interactor 对象](#2.1 Interactor 对象)
- [2.2 Interactable 对象](#2.2 Interactable 对象)
-
- [2.2.1 父子结构](#2.2.1 父子结构)
- [2.2.2 "Hand Grab lnteractable" 脚本](#2.2.2 “Hand Grab lnteractable” 脚本)
- [2.2.3 "Move Towards Target Provider" 脚本](#2.2.3 “Move Towards Target Provider” 脚本)
- [2.2.4 其他 Movement Provider](#2.2.4 其他 Movement Provider)
- [3 抓取配置](#3 抓取配置)
-
- [3.1 玩家配置](#3.1 玩家配置)
-
- [3.1.1 自制预制体](#3.1.1 自制预制体)
- [3.1.2 添加交互功能](#3.1.2 添加交互功能)
- [3.2 物体配置](#3.2 物体配置)
-
- [3.2.1 配置 HandGrabInteractable](#3.2.1 配置 HandGrabInteractable)
- [3.2.2 One Grab Free Transformer](#3.2.2 One Grab Free Transformer)
- [3.2.3 Two Grab Free Transformer](#3.2.3 Two Grab Free Transformer)
- [3.3 颜色高亮显示](#3.3 颜色高亮显示)
- [4 添加物理抛掷效果](#4 添加物理抛掷效果)
- [5 录制抓取手势](#5 录制抓取手势)
-
- [5.1 场景录制](#5.1 场景录制)
- [5.2 工具录制](#5.2 工具录制)
- [5.3 手势镜像和缩放](#5.3 手势镜像和缩放)
- [6 远距离抓取物体](#6 远距离抓取物体)
-
- [6.1 玩家配置](#6.1 玩家配置)
- [6.2 物体配置](#6.2 物体配置)
-
- [6.2.1 抓取配置](#6.2.1 抓取配置)
- [6.2.2 添加物理效果](#6.2.2 添加物理效果)
1 手势抓取方式
1.1 Hand Grab
-
无抓取手势(手和物体会穿模)
适合非实物的抓取,例如魔法球。
- 有抓取手势(给不同形状的物体制作不同的抓取手势)
- Pinch Grab:手指靠近物体,做捏合动作激活。
- Palm Grab:手掌靠近物体,做握持动作激活。
- Pinch Grab + Palm Grab:多个抓取点,每个抓取点对应不同抓取姿势。
适用场景
在需要精准抓取手势或者固定某几个抓取手势时,可使用 Hand Grab。
1.2 Touch Hand Grab
可以在物体表面(准确来说是物体的碰撞体)上的任意位置进行抓取,抓取时手指会贴在物体表面上,可以用任意根手指进行抓取。
相比于 Hand Grab,抓取手势更自由。
1.3 Distance Hand Grab
在远距离通过射线抓取物体,可以将远处的物体抓取到手上,或者在远距离操控物体移动。
2 HandGrabExamples 示例场景
VR 中手势交互需要两个对象:
- Interactor:发起交互的对象。
- Interactable:可以被交互的对象。
2.1 Interactor 对象
展开玩家物体 OVRCamreaRig,其子物体 OVRInteraction 是所有交互功能的父物体:
- OVRHmd:头显交互。
- OVRControllerDrivenHands:手柄交互(手柄控制虚拟手部模型的相关功能)。
- OVRHands:手势追踪交互。
展开 OVRHands > LeftHand > HandInteractorsLeft,可看到左手的 Interactor 共有两个:
- HandPokeInteractor:手指点触交互(手指点击虚拟按钮)。
- HandGrabInteractor:手部抓取交互。
-
HandGrabAPI:检测 Hand Grab 是否选中或者取消选中可抓取物体。即,检测抓取动作是否发生。
-
HandWristPoint:控制在抓取时虚拟手部和现实手部在位置和旋转角度上的偏移。
- Offset:位置偏移。
- Rotation:角度偏移。
-
GripPoint:Palm Grab 的探测范围。
调整 GripPoint 上的 HandWristOffset 脚本参数可以修改探测范围的位置和旋转角度。物体进入探测范围后触发抓取,会被吸到手上。
-
PinchPoint:Pinch Grab 的探测范围。
范围大小取决于 PinchPoint 子物体的 Collider 的碰撞体大小。
-
PinchArea:PinchPoint 的 HandPinchOffset 脚本默认引用 PinchArea , PinchArea 碰撞体的位置会影响 PinchPoint 探测范围的位置。
-
HandGrabVisual :连接 SyntheticHand ,在手部呈现出完整的抓取手势后固定手部姿态,让抓取手势不与物体穿模。
-
HandGrabGlow:控制抓取时手部的特效。
-
Glow Color Grabing:抓取时特效的颜色。
-
Glow Color Hover:靠近时特效的颜色。
-
Glow Type:特效类型。
- Outline:手指轮廓高亮。
- Fill:手指颜色填充。
- Both :结合 Outline 和 Fill。
-
2.2 Interactable 对象
2.2.1 父子结构
可抓取物体上需要添加 "Grabbable" 脚本,以实现在抓取物体的时候控制物体的位移、旋转和缩放。
可抓取物体下有一到多个 HandGrabInteractable 物体,即 Hand Grab 所需要的 Interactable 对象。
在示例场景 HandGrabExamples 中,Interactable 对象被统一存放在 Interactables 路径下。以 SimpleGrab2PalmGrab 为例,其子物体 HandGrabInteractable 挂载了交互脚本 "Hand Grab lnteractable"。
2.2.2 "Hand Grab lnteractable" 脚本
-
Supported Grab Type:抓取方式。
- Palm:捏合抓取。
- Pinch:握持抓取。
- All:二者均可。
-
Pinch / Palm Grab Rules:抓取触发手指的规则。
Thumb、lndex、Middle、Ring、Max:大拇指、食指、中指、无名指、小指。
- Required:标记了 Required 的手指,必须参与到抓取的触发过程中。即,想要抓起一个物体,必须要用到 Required 手指。
- Optional:如果没有 Required 手指,则至少要用到一个标记 Optional 的手指。
- Ignore:标记了 Ignore 的手指,不会被考虑到抓取的触发过程中。
Unselect Mode:取消抓取的判定条件。
- All Released:所有 Required 或者 Optional 手指松开后,视为取消抓取。
- Any Released:有一个 Required 手指松开后,视为取消抓取。
-
Hand Alignment(Unity 中单词拼写错误):决定虚拟手如何变化到对应的抓取手势。
- None:没有对应限制,会穿模。与没有添加 SyntheticHand 效果一样。
- AIign On Grab:靠近物体时(即处于 hover 状态)虚拟手会与物体发生穿模。抓取时手部从物体内部快速调整到物体表面。
- Attract On Hover:靠近物体时虚拟手会被限制在物体外部,不会穿模。抓取时手部直接吸附在物体表面。
- Align Fingers On Hover:靠近物体时虚拟手会慢慢靠近物体。抓取时手部慢慢吸附到物体表面,穿模程度较小。
-
[Optional] ScaIed Hand Grab Poses:为不同大小的手配置相应的抓取手势。
- HandGrab Point:后续专门介绍。
2.2.3 "Move Towards Target Provider" 脚本
在运行程序时,如果当前 HandGrabInteractable 物体上没有挂载 "Move Towards Target Provider" 脚本,"Hand Grab lnteractable" 脚本会自动添加 "Move Towards Target Provider" 脚本,用于控制物体被抓取时朝向手部的移动。
- Travel Speed:移动速度。值越大,移动时间越长。
如果想在程序运行前控制物体朝向手部的移动速度,可以在 HandGrabInteractable 物体上添加 "Move Towards Target Provider" 脚本,并设置其引用。
2.2.4 其他 Movement Provider
(1)Move From Target Provider
抓取时将手部吸附到物体上,而不是物体吸附到手上。
(2)Follow Target Provider
抓取时物体会跟随手部移动,具有阻尼效果。
- Speed:物体跟随速度。
3 抓取配置
3.1 玩家配置
3.1.1 自制预制体
(1)将 《2024-04-01 NO.3 Quest3 手势追踪与玩家角色配置》 文章中 SampleScene 场景下的 OVRCameraRig 进行 Prefab Unpack Completely,之后拖拽到 Assets > Prefabs 文件夹下。
3.1.2 添加交互功能
(1)在 Package 中找到 HandGrabInteractor 预制体,将其拖拽为 SampleScene 场景中 MyOVRCameraRig > OVRInteraction > OVRHands > LeftHand > HandInteractorsLeft 的子物体。并对 RightHand 进行同样的操作。
(2)场景中展开 LeftHand 下的 HandGrabInteractor 对象,将其 Visuals 下的两个子对象 HandGrabVisual 和 HandGrabGlow 激活,并设置对应的引用参数。同样的操作应用于 RightHand(步骤中所有 Left 对象改为 Right)。
- HandGrabVisual:
- Synthetic Hand
<--
OVRLeftHandSynthetic。
- Synthetic Hand
- HandGrabGlow:
- Hand Visual
<--
OVRLeftHandSynthetic > OVRLeftHandVisual。 - Hand Renderer
<--
OVRLeftHandSynthetic > OVRLeftHandVisual > OculusHand_L > l_handMeshNode。 - Material Editor
<--
OVRLeftHandSynthetic > OVRLeftHandVisual > OculusHand_L > l_handMeshNode。
- Hand Visual
(3)找到 LeftHand > HandInteractorsLeft 物体上的 "Best Hover lnteractor Group" 脚本,将 HandInteractorsLeft 物体拖拽入 Interactors 列表中。同样的操作应用于 RightHand > HandInteractorsRight 物体(步骤中所有 Left 对象改为 Right)。
Interactors 列表存储不同种类的 Interactor 脚本。
Interactor Group 保证其列表下的所有 Interactor 在同一时刻只有一个进行交互,而其他 Interactor 暂时失活。
此处 Best Hover Interactor Group 是 Meta XR SDK 中 Interactor Group 的其中一种。其特性是:
-
保证优先级高的 Interactor 进入到 hover 状态,优先级低的 Interactor 会暂时失活。
-
默认情况下,越靠前的列表元素拥有越高的优先级。
3.2 物体配置
3.2.1 配置 HandGrabInteractable
(1)在场景中创建一个桌面(Cube,这里配置了红色材质),桌面上放一个立方体 Cube 用于抓取。
同时,给 Cube 添加 Rigidbody 和 Grabbable 组件,并将其 Box Collider 设置为 Trigger,取消勾选 Rigidbody 的 Use Gravity 选项。
最后,勾选 Grabbable 的 Transfer On Second Selection 选项,以实现左右手交替抓取。
(2)在 Project 中找到 HandGrabInteractable 预制体,将其拖拽为 Cube 的子物体。可以看见 HandGrabInteractable 自动寻找到了其父物体中的 Grabbable 和 Rigidbody。
3.2.2 One Grab Free Transformer
到此,运行程序,可以实现双手抓取移动 Cube。
运行程序时,可以看到 Cube 物体上自动添加了 One Grab Free Transformer 脚本,并且被 Grabbable 脚本引用。
Grabbable 脚本通过 Transformer 来控制物体被抓取时的移动旋转和缩放。默认使用 One Grab Free Transformer 脚本(一只手控制)。
其他 One Grab Transformer:
- One Grab Rotate Transformer:仅控制物体旋转。
- One Grab Scale Transformer:仅控制物体缩放。
- One Grab Translate Transformer:仅控制物体平移。
- One Grab Physics Joint Transformer:相对而言使用的很少。
3.2.3 Two Grab Free Transformer
取消勾选 Cube 挂载的 Grabbable 脚本的 Transfer On Second Selection 选项,并更换 One Grab Free Transformer 脚本为 Two Grab Free Transformer 脚本,即可使用双手操控物体。
若想实现单双手都可操控,则同时添加 One Grab Free Transformer 脚本即可。
3.3 颜色高亮显示
(1)在 Cube 下方新建空子物体 Visuals,用于控制 Cube 的颜色显示。
(2)为 Cube 依次添加以下脚本:
-
Interactable Color Visual:控制物体在各种状态下显示的颜色。
需要设置:
- Interactable View
<--
Material Property Block Editor 脚本。 - Editor
<--
Interactable Group View 脚本。
- Interactable View
-
Material Property Block Editor:关联物体材质。
需要设置:
- Renderers
<--
Cube 的 Mesh Renderer 组件。
- Renderers
-
Interactable Group View:可交互物体组。
需要设置:
- Interactables
<--
HandGrabInteractable。
- Interactables
设置完成后,即可通过改变 Interactable Color Visual 中的 Color State 来更改物体对应状态的颜色。
到此,我们可以对 Cube 进行抓取、移动和旋转,但是没有录制抓取手势。
4 添加物理抛掷效果
(1)取消 Cube 的 Box Collider 的 Trigger 选项,并勾选 Rigidbody 的 Use Gravity。此时,手部可以和 Cube 进行碰撞,手部碰撞体在 MyOVRCameraRig > TrackingSpace > LeftHandAnchor > OVRHandPrefab 下(运行状态下可见)。
这里运行时产生碰撞体是因为先前设置了 Enable Physics Capsules 选项。
(2)为 Cube 添加 Physics Grabbable 脚本,添加时会自动关联 Cube 上的 Rigidbody 和 Grabbable。
(3)将 Cube 上的 Physics Grabbable 脚本关联到 HandGrabInteractable 对象上。
(4)在 Project 中找到 HandVelocityCaIculator,将其拖拽为 LeftHand / RightHand 的子物体。
(5)关联对应的 LeftHand / RightHand。
(6)将 HandVelocityCaIculator 关联到 HandInteractorsLeft > HandGrabInteractor 和 HandInteractorsRight > HandGrabInteractor 对象上。
此时,即可完成 Cube 物理抛掷的效果。
如果在抛掷过程中有穿模现象,可以将 Cube 的 Rigidbody 的 Collision Detection 设置为 Continuous Dynamic,进行连续动态的碰撞检测,但性能消耗会更大。
5 录制抓取手势
5.1 场景录制
录制抓取手势需要在 Unity 编辑器运行模式下进行,并且需要将头显和电脑进行串流(串流部分在 《2024-03-28 NO.1 Quest3 开发环境配置教程-CSDN博客》 中提及)。
(1)打开 HandGrabPoseTool 场景,添加待录制的物体 Cube(这里还添加了一个子物体 Cube)。并确保物体上挂载 Rigidbody 组件和 Grabbable 脚本,没有则手动添加。
这里将 BoxCollider 设置为 Trigger,取消勾选了 Rigidbody 的 Use Gravity 选项,并勾选了 Grabbable 脚本的 Transfer On Second Selection。
(2)运行 Unity,按下按钮,开始做抓取手势动作,3s 后将自动结束录制。若想要延长录制准备时间,可在前方点击按钮进行调整。注意,录制完成后不要急着退出运行模式。
(3)录制完成后,可以看见 Cube 下方多了 HandGrabInteractable 子物体。首先将 Cube 拖拽至 Assets > Prefabs 文件夹下成为预制体,再退出 Unity 运行模式。
(4)在 Unity 编辑模式下,删除场景中原有的 Cube,将刚创建的预制体拖入场景,开始进行手势微调。
点击 HandGrabInteractable > HandGrabPose 物体,找到其挂载的 Hand Grab Pose 脚本,调节其参数以达到想要的手势。
-
Fingers Freedom:手指灵活程度。
- Locked:抓取时,虚拟手指无法跟随现实手指进行弯曲。
- Constrained:抓取时,虚拟手指可以向外弯曲而不能向内。
- Free:抓取时,虚拟手指不受限制。
-
Joint Angles:关节角度。
改变值可旋转每个关节的角度,也可以在 Scene 窗口中直接旋转蓝色圆圈编辑调节。
微调完成后,失活 HandGrabPose 的子物体 Ghost-RightHand(Clone),即可取消显示录制手势。之后抓取物体时,手部会呈现刚录制好的姿势。
5.2 工具录制
点击上方菜单 Oculus > Interaction > Hand Grab Pose Recorder,打开录制窗口。
(1)配置场景中想要录制的手,以及待录制物体的刚体组件。这里选择的是右手。
(2)运行 Unity,在做好手势后用鼠标点击 Record HandGrabPose 按钮即可完成录制。建议录制时将物体的 Collider 选择 Trigger,并取消勾选了 Rigidbody 的 Use Gravity。
由于录制时带着头显,不方便使用鼠标。因此可以配置相应的键盘按键(默认是空格),按下该键盘按键后即可完成录制。使用键盘按键来结束录制时,需要先将鼠标双击
Hand Grab Pose Recorder 录制窗口,以获取焦点。
注意,录制完成后不要急着退出运行模式。
(3)录制完成后,点击 Save To Collection 按钮保存录制结果,再退出 Unity 运行模式。
(4)在 Unity 编辑模式下点击 Load From Collection 按钮,即可获取刚录制的手势。
可以看到,Cube 下方添加了两个 HandGrabInteractable,且其中一个记录了手势。其上方没有手势的 HandGrabInteractable 是因为录制前 Cube 拥有一个 HandGrabInteractable 子物体,在读取结果时将其一并拷贝了进来,这里删除即可。
之后录制时,不需要先给物体配置 HandGrabInteractable,添加好 Rigidbody 后直接录制即可。
5.3 手势镜像和缩放
选中需要镜像手势的父物体 HandGrabInteractable,点击 Create Mirrored HandGrabInteractable 按钮,即可生成镜像手势。
调节 Scaled Hand Grab Poses 下方的 Scale 值,点击 Add HandGrabPose Key with Scale XXX 按钮后,即可生成对应缩放比例的手势。
6 远距离抓取物体
6.1 玩家配置
(1)在 Project 窗口中找到 DistanceHandGrabInteractor 预制体,将其拖拽为 HandInteractorsLeft / HandInteractorsRight 的子物体。同时,将其挂载到 HandInteractorsLeft / HandInteractorsRight 的 Interactors 列表下。
(2)展开 DistanceHandGrabInteractor 的 Visuals 子物体,将其两个子物体激活。并进行如下关联(与 [3.1.2 节](#3.1.2 节) 相同):
- HandGrabVisual:
- Synthetic Hand
<--
OVRLeftHandSynthetic。
- Synthetic Hand
- HandGrabGlow:
- Hand Visual
<--
OVRLeftHandSynthetic > OVRLeftHandVisual。 - Hand Renderer
<--
OVRLeftHandSynthetic > OVRLeftHandVisual > OculusHand_L > l_handMeshNode。 - Material Editor
<--
OVRLeftHandSynthetic > OVRLeftHandVisual > OculusHand_L > l_handMeshNode。
- Hand Visual
同样的操作对 OVRRightHandSynthetic 再执行一遍(步骤中所有 Left 对象改为 Right)。
(3)将先前配置好的 HandVelocityCalculator 关联到 DistanceHandGrabInteractor 上。该步骤为可选,但需注意左右手对应配置。
6.2 物体配置
6.2.1 抓取配置
参考 [3.2.1 节](#3.2.1 节) 内容,创建一个 Cube,添加 Rigidbody 和 Grabbable 脚本,并将其设置为 Trigger,取消 Use Gravity。
之后,为该物体添加 Distance Hand Grab Interactable 脚本,添加时会自动关联 Cube 上的 Rigidbody 和 Grabbable。
此时运行 Unity,双手对准 Cube 握拳后,Cube 会被远距离抓到手上。这是因为 Cube 上 Interactable 脚本的 Supported Grab Types 和 RightHand 下 Interactor 的 Supported Grab Types 都是 Pinch。 如果不匹配,则不会被抓取。
6.2.2 添加物理效果
(1)准备模型,这里使用两个 Cube 当成一把剑。其父子结构如下图。
给 Sword 添加 Rigidbody 和 Grabbable 组件,同时添加 Physics Grabbable 脚本。
Cube 和 Cube (1) 上带有 BoxCollider,且均设置为 Trigger。
(2)进行手势录制,并做一些微调。这里将所有手指都设置为 Locked。
(3)在生成的 HandGrabInteractable 物体上添加 Distance Hand Grab Interactable 脚本,并为其关联 Physics Grabbable 脚本。
对于 Hand Grab Interactable 脚本,其 Physics Grabbable 的引用在录制时被自动关联,因此这里不需要再重复关联。也可以手动检查确认一下。
(4)恢复物体的 Use Gravity,并且将 Collider 的 Trigger 选项取消。此时这把"剑"便拥有了远距离抓取和抛掷的功能。