Unity VR开发教程 OpenXR+XR Interaction Toolkit 2.1.1(七)射线抓取

news2025/2/28 19:49:08

文章目录

  • 📕教程说明
  • 📕添加射线功能的相关组件
  • 📕设置 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 物体上分别挂载这个脚本,并且手动赋值。

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

最终效果:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/18979.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Node.js安装及环境配置

Node.js安装及环境配置1.下载安装Node.js2.npm安装路径配置3.环境变量配置4.换源5.测试npm安装1.下载安装Node.js Node.js官网 下载如图所示版本:(请根据自己的系统环境选择) 下载完成后傻瓜式安装即可 测试环境: PS C:\Users…

终于拿到了爆火全网的进一线大厂程序员必看的1700道java面试题

爆火全网的进一线大厂程序员必看的1700道java面试题到底有多牛? 牛不牛不敢说,但是有好多程序员是靠这一套1700道高频面试题,顺利收到很多大厂offer! 以至于,到现在为止,大厂都开始按照这一套1700道面试题…

java计算机毕业设计ssm美食视频教学网站element 前后端分离

项目介绍 高校实验室信息管理平台是使用JAVA的SSM技术,MySQL作为数据库开发,用户通过查看实验室信息,在线预约实验室,实现高校实验室信息化管理。首先对本论文进行分析后,提出平台的相关技术,然后整理系统的需求分析,根据需求进行功能和数据库设计,最后进行系统实现和测试 。 …

Redis的Java客户端

目录 1 前言 2 Jedis客户端 2.1 jedis快速入门 2.2Jedis连接池 3 SpringDataRedis 3.1快速入门 3.2ReisTemplate配置序列化工具 3.3 StringRedisTemplate 1 前言 在Redis官网中提供了各种语言的客户端,地址:https://redis.io/resources/clien…

螺栓防松设计

常用的防松方法有三种:摩擦防松、机械防松和永久防松。机械防松和摩擦防松称为可拆卸防松,而永久防松称为不可拆卸防松。常用的永久防松有:点焊、铆接、粘合等,这种方法在拆卸时大多要破坏螺纹紧固件,无法重复使用。常…

系统测试-从研发到测试过程

系统测试是为了发现错误而执行程序的过程,成功的测试是发现了至今尚未发现的错误的测试。目的是在真实系统工作环境下通过与系统的需求定义作比较,检验完整的软件配置项能否和系统正确连接,发现软件与系统/子系统设计文档和软件开发合同规定不…

[Redis] Redis实战--EVAL

✨✨个人主页:沫洺的主页 📚📚系列专栏: 📖 JavaWeb专栏📖 JavaSE专栏 📖 Java基础专栏📖vue3专栏 📖MyBatis专栏📖Spring专栏📖SpringMVC专栏📖SpringBoot专…

基于移动品台的产品追溯系统设计与实现

项目描述 临近学期结束,还是毕业设计,你还在做java程序网络编程,期末作业,老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下,你想解决的问…

骚戴独家笔试---算法篇

链表 反转链表 /* public class ListNode { int val; ListNode next null;ListNode(int val) { this.val val; } }*/ import java.util.Stack; public class Solution {public ListNode ReverseList(ListNode head) {Stack<ListNode> stack new Stack<>();//把…

进程间通信:无名管道+有名管道

进程间通信&#xff08;Inter-Process Communication&#xff09; 为什么需要进程间通信 当程序是多进程协同工作时&#xff0c;进程间基本都会涉及到数据共享 如何实现进程间数据的共享? 使用进程间通信来实现数据共享 进程间有时需要传递消息 --但是进程在系统有自己的地址…

msdn下载的系统怎么安装

有小伙伴们不知道安装msdn系统的具体操作&#xff0c;那么小编就教大家下载的系统怎么安装吧。 工具/原料&#xff1a; 系统版本&#xff1a;win10 专业版 品牌型号&#xff1a;联想小新Air 13 Pro 软件版本&#xff1a;小鱼一键重装系统v3.1.329.319 方法/步骤&#xff1…

UNIAPP实战项目笔记41 收货地址页面布局和省市县三级联动

UNIAPP实战项目笔记41 收货地址页面布局和省市县三级联动 my-add-path.vue 设置页面布局 具体内容图片自己替换哈&#xff0c;随便找了个图片的做示例 用到了vue的默认组件 城市选择器mpvueCityPicker,从uniappDemo中复制过来即可,具体位置见目录结构 代码 my-add-path.vue 页…

数据结构二叉排序树应用一

2022.11.19 二叉排序树应用一任务描述相关知识编程要求测试说明C/C代码任务描述 本关任务&#xff1a;输入一个无序序列&#xff0c;创建一棵二叉排序树。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;1.二叉排序树定义&#xff0c;2.如何创建一棵二叉排序…

黑*头条_第6章_admin端功能开发通用后端封装

黑*头条_第6章_admin端功能开发&通用后端封装 文章目录黑*头条_第6章_admin端功能开发&通用后端封装admin端功能开发&通用后端封装今日目标1 admin端的登录功能实现1.1 admin项目搭建1.2 登录接口-后端1.2.1接口定义1.2.2mapper定义1.2.3 代码编写1.3 前端项目导入…

FCN的代码解读

目录 模型初始化 VGG初始化 FCN初始化 图片的预处理 图片处理 图片编码 计算相关参数 模型训练 一个小问题 完整代码 参考 最近浅研究了一下关于图像领域的图像分割的相关知识&#xff0c;发现水还是挺深的&#xff0c;因为FCN差不多也是领域的开山鼻祖&#xff0c;所以就先从…

Postman进阶篇(十)-在pre-request script或test script中使用pm对象访问变量

在之前的文章中介绍过postman中的两个脚本——pre-request script或test script&#xff0c;在这两个脚本中都有使用到pm对象。&#xff08;pre-request script详细介绍、Test script详细介绍&#xff09;pm对象是在postman的脚本中非常重要&#xff0c;也是十分常用的方法。本…

Tomcat使用与Servlet

目录 Tomcat Get与Post请求的区别 Servlet 体系 请求流程 生命周期 配置loadOnStartup 线程安全问题 问题原因 解决方案 Servlet核心类 请求 响应 转发与重定向 转发 重定向 Cookie与HttpSession Cookie HttpSession ServletConfig ServletContext 总结 F…

基于javaweb,ssm学生宿舍管理系统(带论文)

开发工具&#xff1a;IDEA 服务器&#xff1a;Tomcat8.0&#xff0c; jdk1.8 项目构建&#xff1a;maven 数据库&#xff1a;mysql5.7 系统分前后台&#xff0c;非前后端分离 前端技术&#xff1a;vue.jselementUI等框架实现 服务端技术&#xff1a;springspringmvcmybat…

Java--Lambda(1)简介

文章目录0 写在前面1 优点2 实现3 写在末尾0 写在前面 阅读公司前辈写的代码的时候&#xff0c;有一些地方总不理解&#xff0c;后来才知道是Lambda表达式。 所以学习了一下&#xff0c;在此记录一下。 通过百度搜索得知&#xff1a;Lambda 表达式也叫作匿名函数&#xff0c…

简单易懂的 全景图高清下载方法以及原理简要解析(支持下载建E、720yun、酷雷曼、景站、酷家乐、百度街景原图)

简单易懂的 全景图高清下载方法以及原理简要解析&#xff08;支持下载建E、720yun、酷雷曼、景站、酷家乐、百度街景原图&#xff09; 文章目录简单易懂的 全景图高清下载方法以及原理简要解析&#xff08;支持下载建E、720yun、酷雷曼、景站、酷家乐、百度街景原图&#xff09…