Unity VR开发教程 OpenXR+XR Interaction Toolkit 2.1.1(七)射线抓取
创始人
2024-02-01 19:02:06

文章目录

  • 📕教程说明
  • 📕添加射线功能的相关组件
  • 📕设置 Interaction Layer Mask
  • 📕让 XR Direct Interactor 不对 XR Ray Interactor 产生干扰
  • 📕使抓取的物体不会吸到手上
  • 📕远距离抓取时通过摇杆改变抓取物体的位移和旋转角度

往期回顾:
Unity VR开发教程 OpenXR+XR Interaction Toolkit 2.1.1 (一) 安装和配置
Unity VR开发教程 OpenXR+XR Interaction Toolkit 2.1.1 (二) 手部动画
Unity VR开发教程 OpenXR+XR Interaction Toolkit 2.1.1 (三) 转向和移动
Unity VR开发教程 OpenXR+XR Interaction Toolkit 2.1.1 (四) 传送
Unity VR开发教程 OpenXR+XR Interaction Toolkit 2.1.1 (五) UI
Unity VR开发教程 OpenXR+XR Interaction Toolkit 2.1.1 (六)手与物品交互(触摸、抓取)

VR 中的抓取功能除了近距离地与物体接触将其抓起(我把它称为 “直接抓取”)之外,还有一种通过射线抓取的方式。当射线射到物体上时,按下抓取键就能将其抓起。本篇教程,我将介绍如何用 XR Interaction Toolkit 实现射线抓取。


📕教程说明

使用的 Unity 版本: 2020.3.36

使用的 VR 头显: Oculus Quest 2

前期的配置:环境配置参考教程一,手部模型参考教程二。本篇教程的场景基于上一篇教程搭建的场景进行延伸。

项目源码(持续更新):https://github.com/YY-nb/Unity_XRInteractionToolkit_Tutorial2022/tree/master

最终实现的效果:

在这里插入图片描述


回顾上一篇直接抓取的教程,VR 中的交互一般需要两个对象:一个是可交互的对象(Interactable),一个是发起交互的对象(Interactor,一般是玩家自己)。直接抓取和射线抓取所抓取的对象并没有区别,因此 Interactable 需要的组件可以参考上一篇教程:Unity VR开发教程 OpenXR+XR Interaction Toolkit 2.1.1 (六)手与物品交互(触摸、抓取)

它们的区别正是 Interactor 的不同。直接抓取使用的是 XR Direct Interactor;射线抓取使用的是射线,如果你看过此系列教程的前几篇,应该对一个叫做 “XR Ray Interactor” 的组件有点印象,我们在传送和 UI 交互功能中有用到这个组件。它的功能是通过射线检测实现与物体的远距离交互,因此这个组件就是远距离抓取需要的 Interactor。


📕添加射线功能的相关组件

还是沿用上一篇教程中搭建好的场景。

在这里插入图片描述

我们在 XR Origin 下的 Ray Controller 物体创建两个子物体,可以鼠标右键点击 Ray Controller,选择 XR -> Ray Interactor(Action-based):

在这里插入图片描述

观察这两个物体的 Inspector 面板,可以发现其中具备了 XR Interaction Toolkit 射线操作中需要用到的 XR Controller,XR Ray Interactor,Line Renderer 和 XR Interactor Line Visual

在这里插入图片描述

我们先找到 XR Controller(Action-based),点击下图中用红框标出的按钮,选择左手或右手的 Preset:

在这里插入图片描述
在这里插入图片描述
选择后 XR Controller 下就会有对应的 Reference。左右手的 XR Controller 都设置完毕后,为了更好地区分左右手,我们可以修改刚刚创建的两个子物体的名字,分别为 LeftHand DistanceGrabController 和 RightHand DistanceGrabController :

在这里插入图片描述
以上操作是教大家如何从零创建一个能够发射射线的 Interactor 物体。为了能有更好的效果,我们还可以设置 XR Ray Interactor 上的射线发射起始点 Ray Origin Transform 和 XR Interactor Line Visual 的颜色效果,这一部分可以参考 UI 交互的教程(Unity VR开发教程 OpenXR+XR Interaction Toolkit 2.1.1 (五) UI),我这边就直接模仿 LeftHand UIController 和 RightHand UIController 的设置,让射线起始点位于食指处,射线无效是显示为透明(Invalid Color Gradient)。

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


📕设置 Interaction Layer Mask

现在我们试着运行一下程序,当我们手部发出的射线射到可抓取的物体上时,按下手柄的抓取键就能将物体抓到手上。但是射线射到地面上时居然激活了传送。

在这里插入图片描述

这个问题曾经在 UI 交互的 Demo 中出现过,当时我们是修改 XR Ray Interactor 的 Raycast Mask 来过滤能被 UI 射线射到的目标。这种方式需要更改物体的 Physics Layer。

然而,我们还有一种解决方法,这里就要用到上一篇教程中提到的 Interaction Layer Mask它依靠 Interactor 和 Interactable 中的 Interaction Layer 决定了 Interactor 能与哪些 Interactable 发生交互;而 Raycast Mask 是 XR Ray Interactor 特有的,它依靠物体的 Physics Layer(Inspector 面板最上方的那个 Layer)来决定射线能射到哪些物体的碰撞体上

因为 XR Ray Interactor 的 Interaction Layer Mask 默认是 Everything,地面的 Interaction Layer Mask 是 Teleport(我们在之前的教程中修改过),Teleport 包含在 Everthing 中,所以射线抓取的射线能与传送地面交互。而可交互物体的 Interaction Layer Mask 默认是 Default,所以只要把 XR Ray Interactor 的 Interaction Layer Mask 改为 Default,就能解决这个问题。

在这里插入图片描述

当然,你也可以为直接抓取和射线抓取分别设立一个 Interaction Layer,来区分哪些物体能被直接抓取,哪些能被射线抓取,或者哪些既能被直接抓取,又能被射线抓取(Interaction Layer Mask 可以多选)

效果图:

在这里插入图片描述


📕让 XR Direct Interactor 不对 XR Ray Interactor 产生干扰

但是此时还有一个 BUG,如上图所示,当我们用射线抓取了一个物体后,即使按住手柄 Grip 键不放,有时候也会不小心将另一个物体 “吸” 过来。

实际上,这是 XR Direct Interactor 对 XR Ray Interactor 产生了干扰。我这个场景中的物体既能被射线抓取,又能被直接抓取。当我用射线抓取将物体吸到手上时,XR Direct Interactor 会认为它抓到了物体,因为手柄的 Grip 键处于按下的状态,而且物体位于 XR Direct Interactor 的触发区域,所以触发了它的功能,而且此时这个被抓取的物体就不是由 XR Ray Interactor 管了,而是归 XR Direct Interactor 管

与此同时,XR Ray Interactor 会继续发挥它的作用,通过射线检测判断是否与可交互的物体发生交互,所以它仍然会从起始点开始发射射线。我们在场景中看不到射线是因为 XR Interactor Line Visual 脚本只能渲染从起始点到第一个有效物体的碰撞体的那一部分射线,因为射线射到的第一个有效的物体是刚刚抓取的第一个物体,所以我们看不到渲染的射线,但是没被渲染的那部分射线仍然发挥着检测作用。所以当射线检测到另一个可交互的物体时,XR Ray Interactor 仍会发挥它的抓取作用。

解决思路也很简单,就是当 XR Direct Interactor 发挥作用时,将 XR Ray Interactor 隐藏掉。我们可以写一个脚本进行控制:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;public class GrabRayController : MonoBehaviour
{public XRRayInteractor leftGrabRay;public XRRayInteractor rightGrabRay;public XRDirectInteractor leftDirectGrab;public XRDirectInteractor rightDirectGrab;void Update(){leftGrabRay.enabled = leftDirectGrab.interactablesSelected.Count == 0;rightGrabRay.enabled = rightDirectGrab.interactablesSelected.Count == 0;}
}

然后把这个脚本随便挂在一个物体上,我这里挂在在 RayController上。然后在 Inspector 面板中对变量进行赋值,那么这个 BUG 就被解决了。

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


📕使抓取的物体不会吸到手上

刚刚的 Demo 中,当射线射到可交互的物体后按下手柄的 Grip 键,物体会被立刻吸到手上。如果我想要抓取的时候让物体和手保持一定距离,营造一种远程操纵物体的感觉要怎么办呢?

我们找到 XR Ray Interactor 脚本,将 Force Grab 取消勾选

在这里插入图片描述
如果想要让射线射到物体上的点作为实际的抓取点,而不是默认的抓取点,可以将 Dynamic Attach 勾选(上一篇直接抓取的教程中有介绍)

在这里插入图片描述

最终效果:

在这里插入图片描述


📕远距离抓取时通过摇杆改变抓取物体的位移和旋转角度

当你在测试射线抓取的场景时,也许会发现如果使用取消 Force Grab 的射线抓取方式,当你按下手柄的 Grip 键抓取物体再推动摇杆,物体的位移和旋转角度为发生变化,如下图所示:

这是由 XR Controller(Action-based)和 XR Ray Interactor 组件的设置引起的。

首先看 XR Controller(Action-based)上的 Rotate Anchor Action 和 Translate Anchor Action:

在这里插入图片描述

它们引用了 Input System 中的 Action,我们找到默认的输入配置文件:

在这里插入图片描述
找到 Rotate Anchor 和 Translate Anchor:

在这里插入图片描述
可以看到这两个动作和摇杆在平面坐标轴上的位置有关,我们点击 Primary2DAxis,观察右侧的面板。

Rotate Anchor:

在这里插入图片描述

Translate Anchor:

在这里插入图片描述

关注其中的 Scale Vector 2,Rotate Anchor 的 X 是 1,说明在 X 轴上(左右)推动摇杆会触发这个动作;Translate Anchor 的 Y 是 1,说明在 Y轴上(前后)推动摇杆会触发这个动作。

然后我们再看 XR Ray Interactor 上的 Anchor Control:

在这里插入图片描述
正是因为这些设置,当我们远程抓取物体时,前后推动摇杆能控制物体与手的距离,左右推动摇杆能控制物体的旋转角度。

实际效果:

在这里插入图片描述

但是可以发现,触发 Rotate Anchor 或 Translate Anchor 的时候也会触发其他与摇杆控制有关的动作,比如传送,持续移动。因此我们希望在对远距离抓取物体进行操作时,不会触发该手柄其他和摇杆控制绑定的动作。

我们可以修改刚刚写的 Grab Ray Controller 脚本:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.XR.Interaction.Toolkit;public class GrabRayController : MonoBehaviour
{public XRRayInteractor grabRayInteractor;public XRDirectInteractor grabDirectInteractor;public InputActionReference rotateAnchorReference;public InputActionReference translateAnchorReference;public InputActionReference teleportActivateReference;public InputActionReference moveReference;public InputActionReference turnReference;private void Start(){grabRayInteractor.selectEntered.AddListener(OnEnterGrab);grabRayInteractor.selectExited.AddListener(OnExitGrab);}private void OnDestroy(){grabRayInteractor.selectEntered.RemoveListener(OnEnterGrab);grabRayInteractor.selectExited.RemoveListener(OnExitGrab);}private void OnEnterGrab(SelectEnterEventArgs arg){DisableAction(teleportActivateReference);DisableAction(moveReference);DisableAction(turnReference);EnableAction(rotateAnchorReference);EnableAction(translateAnchorReference);}private void OnExitGrab(SelectExitEventArgs arg){EnableAction(teleportActivateReference);EnableAction(moveReference);EnableAction(turnReference);DisableAction(rotateAnchorReference);DisableAction(translateAnchorReference);}void Update(){grabRayInteractor.enabled = grabDirectInteractor.interactablesSelected.Count == 0;       }private void EnableAction(InputActionReference actionReference){var action = GetInputAction(actionReference);if (action != null && !action.enabled)action.Enable();}private void DisableAction(InputActionReference actionReference){var action = GetInputAction(actionReference);if (action != null && action.enabled)action.Disable();}private InputAction GetInputAction(InputActionReference actionReference){return actionReference != null ? actionReference.action : null;}
}

核心思想是在触发抓取(SelectEnter 事件触发)和取消抓取(SelectExit 事件)的时候去控制相关的 InputAction 是否能开启。

因为我把这个脚本改成了左右手通用的模式,所以我们要把刚刚挂载到 RayController 物体上的 GrabRayController 脚本移除,改成在 LeftHand Controller 和 RightHand Controller 物体上分别挂载这个脚本,并且手动赋值。

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

最终效果:

在这里插入图片描述

相关内容

热门资讯

应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...