Unity Meta Quest 一体机开发(十三):【手势追踪】自定义交互事件 EventWrapper
文章目录
- 📕教程说明
- 📕交互事件概述
- 📕自定义交互逻辑
- ⭐方法一:Inspector 面板赋值
- ⭐方法二:纯代码处理
此教程相关的详细教案,文档,思维导图和工程文件会放入 Spatial XR 社区。这是一个高质量 XR 社区,博主目前在内担任 XR 开发的讲师。此外,该社区提供教程答疑、及时交流、进阶教程、外包、行业动态等服务。
社区链接:
Spatial XR 高级社区(知识星球)
Spatial XR 高级社区(爱发电)

📕教程说明
这篇教程将会介绍如何用 Meta XR SDK,自定义交互事件触发时执行的逻辑。最后会实现一个功能:用手指点击 UI 按钮后,在前方召唤一把剑。
环境配置可参考:https://blog.csdn.net/qq_46044366/article/details/133967343
配置一个基本的玩家物体可以参考前几期教程:https://blog.csdn.net/qq_46044366/article/details/134097455
系列教程专栏:https://blog.csdn.net/qq_46044366/category_12118293.html
配套的视频链接:
上半部分:https://www.bilibili.com/video/BV1rK4y1B7Zs
下半部分:https://www.bilibili.com/video/BV1vT4y1p7EK
电脑操作系统:Windows 11
使用的 VR 设备:Meta Quest 3(Quest 系列都适用)
使用的 Unity 版本:2021.3.5 LTS (这里推荐使用 2021 及以上的 LTS 版本)
Meta XR SDK 版本:v57
官方文档:https://developer.oculus.com/documentation/unity/unity-gs-overview/
Event Wrapper 介绍文档:
https://developer.oculus.com/documentation/unity/unity-isdk-event-wrappers/
最终效果:

📕交互事件概述
交互事件是 XR 开发中经常使用的一个东西。当一个交互动作发生的时候,可能会伴随着一些事情的发生。比如点击一个 “游戏开始” 的UI按钮,就会触发 “游戏开始” 事件,从而对游戏场景里的一些东西进行初始化。这个时候 “点击UI按钮” 就是一个交互事件,对游戏场景里的东西初始化就是这个事件触发后需要执行的事。在 Meta XR SDK 中,交互事件一般会在一个交互过程中的不同状态下触发,以点击 UI 按钮为例,这个动作属于 Poke 交互,我们知道交互的发生需要有 Interactor 和 Interactable 两个对象的参与,那么 Poke 交互对应的 Interactor 在手上,对应的 Interactable 在 UI 按钮物体上。对于 UI 按钮这个 Interactable 来说,当手指靠近按钮的时候,按钮自身会进入到 Hover 状态,这个时刻就可以当作一个交互事件发生了。然后当手指点击到按钮,将按钮按到底的时候,按钮自身会进入到 Select 状态,这边需要注意的是对于一个可以被推动的按钮来说,必须要推到底,也就是推到按钮 Surface 的位置才会进入到 Select 状态,当手指刚刚触碰到按钮表面的时候是不会进入到 Select 状态的。那么进入到 Select 状态这个瞬间也可以当作一个交互事件发生了。
其实前几期教程我们已经有接触到交互事件,我这里是在场景中添加了一个 Poke 教程里配置的 UI 按钮(教程链接:Unity Meta Quest 一体机开发(十二):【手势追踪】Poke 交互 - 用手指点击由 3D 物体制作的 UI 按钮)。

比如按钮身上的 Interactable Color Visual 脚本,这个脚本能够实现物体在进入不同状态的时候改变物体的颜色。那么这就和交互事件的概念比较像了,进入到不同的状态相当于不同交互事件的触发,改变颜色相当于事件触发后执行的事。Meta XR SDK 会自动检测相应的交互事件什么发生,但是这个脚本把交互事件触发后执行的逻辑写死了,它只能改变物体的颜色,无法处理其他的事。而接下来我会介绍如何去自定义交互事件触发时执行的逻辑。
📕自定义交互逻辑
Meta XR SDK 提供了一个 EventWrapper 的概念,叫做事件包装器(官方文档:https://developer.oculus.com/documentation/unity/unity-isdk-event-wrappers/)。它能够处理 Interaction SDK 中的一些交互事件,并且我们能够自定义交互事件发生时执行的逻辑。如下图所示,Meta 提供了不同类型的 EventWrapper:

我们这期教程主要关注可交互物体,也就是 Interactable 的交互事件,那么可以使用这两种 EventWrapper:InteractableUnityEventWrapper 和 PointableUnityEventWrapper。这两个脚本有一些相同的交互事件,也有一些不同的交互事件。不过最常用的是下图中标出的 4 个共同的交互事件:

Hover 是悬停的状态,Interactor 靠近 Interactable 的时候触发。当交互动作完成的时候,也就是 Interactor 选中 Interactable 时,由 Hover 状态转变为 Select 状态。
我们这期教程会介绍 InteractableUnityEventWrapper 脚本的用法,它用起来会稍微简单一点。
我们需要把 InteractableUnityEventWrapper 脚本添加到 UI 按钮物体上:

我们可以在脚本的 Inspector 面板上看到这个脚本提供的交互事件。
⭐方法一:Inspector 面板赋值
Event Wrapper 提供交互事件,并且检测交互事件什么时候触发。但是触发交互事件后需要执行什么事情需要我们自己处理。第一种处理方式是在 Event Wrapper 脚本的 Inspector 面板上直接进行赋值。我们会实现按下 UI 按钮后在前方生成一把剑的功能,判断什么时候按下 UI 按钮会由 Event Wrapper 来处理,我们这里把按下 UI 按钮视为 Select 交互事件触发,也就是将按钮按到底的时候,触发 Select 事件,然后执行生成剑的逻辑。那么我们需要自己写个脚本实现生成物体的功能,然后让 Event Wrapper 在判断到 Select 事件触发时去调用我们脚本中生成物体的方法。
脚本如下:
using Oculus.Interaction;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class ItemSpawner : MonoBehaviour
{public GameObject spawnObj;public Transform spawnPoint;public void SpawnItem(){GameObject obj = GameObject.Instantiate(spawnObj);obj.transform.position = spawnPoint.position;}
}
spawnObj 是需要生成的物体,spawnPoint 是物体生成的位置,SpawnItem 方法负责将需要生成的物体生成在需要生成的位置上。
然后将这个脚本挂载到按钮物体身上,并且在面板上进行赋值:

我这边将一个剑的模型制作成了 Prefab,拖到了 spawnObj 变量上。然后在按钮物体上新建了一个子物体,作为生成剑的位置。

我们可以在场景中调整 SpawnPoint 子物体的位置。
然后点击 InteractableUnityEventWrapper 脚本上的 WhenSelect 事件的 “+”号,进行事件触发执行逻辑的添加:



当按钮被按到底的时候, WhenSelect 事件触发,然后调用 SpawnButton 物体上的 ItemSpawner 脚本中的 SpawnItem 方法,在设定的生成点生成剑的游戏物体。
⭐方法二:纯代码处理
我们修改 ItemSpawner 脚本:
using Oculus.Interaction;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class ItemSpawner : MonoBehaviour
{public GameObject spawnObj;public Transform spawnPoint;private InteractableUnityEventWrapper eventWrapper;void Start(){eventWrapper = GetComponent<InteractableUnityEventWrapper>();eventWrapper.WhenSelect.AddListener(SpawnItem);}private void OnDestroy(){eventWrapper.WhenSelect.RemoveListener(SpawnItem);}public void SpawnItem(){GameObject obj = GameObject.Instantiate(spawnObj);obj.transform.position = spawnPoint.position;}
}
和方法一的区别是,我们在代码中获取了 InteractableUnityEventWrapper 脚本,然后用代码为 InteractableUnityEventWrapper 的 WhenSelect 事件添加了事件触发后需要执行的方法。
InteractableUnityEventWrapper 的 WhenSelect 事件的监听器(AddListener 方法)需要传入一个 UnityAction 类型的参数。UnityAction 是 Unity 中的一种委托类型,可以绑定无返回值,无参数的方法。我们写的 SpawnItem 方法正好符合条件,所以能直接传入 AddListener 方法中,相当于我们把 SpawnItem 方法的执行委托给了 Event Wrapper,当 Event Wrapper 检测到 WhenSelect 事件触发后,会执行我们委托的事情,也就是调用 SpawnItem 方法,在设定的生成点生成指定的物体。
另外需要注意的是,当我们调用了 AddListener 后,需要在合适的地方调用 RemoveListener 方法,将事件监听器移除掉。
如果我们选用纯代码的方法处理交互事件,就不用在 Unity 的 Inspector 面板中拖拽赋值了。然后 ItemSpawner 脚本需要和 InteractableUnityEventWrapper 脚本挂载到同一个游戏物体身上。
最终效果:

相关文章:
Unity Meta Quest 一体机开发(十三):【手势追踪】自定义交互事件 EventWrapper
文章目录 📕教程说明📕交互事件概述📕自定义交互逻辑⭐方法一:Inspector 面板赋值⭐方法二:纯代码处理 此教程相关的详细教案,文档,思维导图和工程文件会放入 Spatial XR 社区。这是一个高质量…...
13、Redis高频面试题
1、项目中为什么用Redis 我们项目中之所以选择Redis,主要是因为Redis有下面这些优点: 操作速度快:Redis的数据都保存在内存中,相比于其它硬盘类的存储,速度要快很多数据类型丰富:Redis支持 string&#x…...
Koa学习笔记
1、npm 初始化 npm init -y生成 package.json 文件,记录项目的依赖2、git 初始化 git init生成 .git 隐藏文件夹,.git 的本地仓库创建 .gitignore 文件,添加不提交文件的名称3、创建 ReadMe.md 文件 记录项目笔记4、搭建项目 安装 Koa 框架npm install koa5、编写最基本的…...
HiDataPlus 3.3.2-005 搭建(个人的一点心得体会 x86 平台)
HDP 集群搭建 前置安装 yum -y install createrepo yum install -y lrzsz yum install -y wget yum install -y vim修改当前集群机器的主机名 hostnamectl set-hostname XXX 这里的 XXX 就是要设置的当前机器的主机名称。主机名称是集群唯一的,一定不要重复&am…...
【PHP】PHP实现与硬件串口交互,接收硬件发送的实时数据
一、前言 目的:借助虚拟串口软件(VSPD)模拟硬件串口发送数据,使用PHP语言实现接收硬件发送的数据。 我这里的需求是连接天平,把天平的称量数据实时的传送到PHP使用。 使用工具:vspd串口调试工具 使用语…...
HNU-数据库系统-作业
数据库系统-作业 计科210X 甘晴void 202108010XXX 第一章作业 10.09 1.(名词解释)试述数据、数据库、数据库管理系统、数据库系统的概念。 数据,是描述事物的符号记录。 数据库(DB),是长期存储在计算机内、有组织、可共享的大量…...
Python基础知识:整理10 异常相关知识
1 异常的捕获 1.1 基础写法 """基本语法:try:可能发生错误的代码except:如果出现异常,将执行的代码""" try:fr open("D:/abc.txt", "r", encoding"utf-8") except:print("出现异常…...
golang并发安全-select
前面说了golang的channel, 今天我们看看golang select 是怎么实现的。 数据结构 type scase struct {c *hchan // chanelem unsafe.Pointer // 数据 } select 非默认的case 中都是处理channel 的 接受和发送,所有scase 结构体中c是用来存储…...
微软Visual Studio产品之Visual C++编程进阶——一维数组(画画版)
我是荔园微风,作为一名在IT界整整25年的老兵,看到不少初学者在学习编程语言的过程中如此的痛苦,我决定做点什么,我小时候喜欢看小人书(连环画),在那个没有电视、没有手机的年代,这是…...
Moonbeam生态项目分析 — — 下一代DeFi协议HydraDX
作者:David 概览 HydraDX是一个基于Substrate区块链框架构建的DeFi协议,旨在为波卡带来大量流动性。HydraDX的特色服务,HydraDX Omnipool,是一种创新的自动化做市商(AMM),通过将所有资产组合在…...
Spark九:Spark调优之Shuffle调优
Spark shuffle调优方法 map端和reduce端缓存大小设置,reduce端重试次数和等待时间间隔,以及bypass设置 学习资料:https://mp.weixin.qq.com/s/caCk3mM5iXy0FaXCLkDwYQ 一、map和reduce端缓冲区大小 1.1 map端 在Spark任务运行过程中&…...
linux c多线程优先级
在 Linux 系统中,可以使用 pthread_setschedparam 函数来设置线程的优先级。该函数需要传入一个指向 pthread_t 类型的线程 ID,以及一个指向 struct sched_param 类型的结构体对象。struct sched_param 结构体包含了线程的优先级信息。 下面是一个示例代…...
Redis在项目开发中的应用
Spring Boot集成Redis构建博客应用 在这个示例中,我们将展示如何使用Spring Boot和Redis构建一个简单的博客应用,包括文章发布、点赞和评论功能。 1. 添加依赖 首先,我们需要在pom.xml文件中添加Spring Boot和Redis的依赖项。 <!-- Sp…...
mapper向mapper.xml传参中文时的乱码问题
1.起因: 在idea中进行模糊查询传参时,发现在idea中查中文查不出记录,在navicate中可以查出来。 2.猜测: 1.idea中的编码问题导致的乱码。 2.idea和navicate的编码一致性导致的乱码。 3.mapper向mapper.xml传参后出现乱码。 3.解…...
基于Docker官方php:7.1.33-fpm镜像构建支持67个常见模组的php7.1.33镜像
实践说明:基于RHEL7(CentOS7.9)部署docker环境(23.0.1、24.0.2),所构建的php7.1.33镜像应用于RHEL7-9(如AlmaLinux9.1),但因为docker的特性,适用场景是不限于此的。 文档形成时期:2017-2023年 因系统或软件版本不同&am…...
Type-C PD充电器受电端sink诱骗取电汇总:小家电应用5V9V12V15V20V28V
小家电产品、美容产品、电动产品等升级采用Type-C接口,在Type-C接口上使用Type-C取电芯片,即可使用快速充电器的5V、9V、12V、15V、20V供电,无需再配充电器,各类品牌的充电器都可以支持。目前充电器常见的USB-PD功率为:…...
禁用code server docker容器中的工作区信任提示
VSCode 添加受限模式,主要是防止自动运行代码的,比如在vscode配置的task和launch参数是可以运行自定义代码的。如果用VScode打开未知的工程文件就有可能直接运行恶意代码。 但是当我们的实验基础模板文件可控的情况下,要想禁用code server do…...
JSON格式插件-VUE
JsonEditor 安装: npm i bin-code-editor -S引入: import Vue from vue; import CodeEditor from bin-code-editor; import bin-code-editor/lib/style/index.css; import App from ./App.vue; Vue.use(CodeEditor); new Vue({el: #app,render: h > …...
dubbo的springboot集成
1.什么是dubbo? Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力, 利用 Dubbo …...
【人工智能】智能电网:未来能源的革命
未来能源的革命 智能电网革命的意义在于将电力行业从传统的集中式发电和集中式输配电模式转变为智能化、分布式、互动式的能源网络。 现在我们从以下方面详细认真的了解一下智能电网: 智能变电站,智能配电网,智能电能表,智能交互…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...
Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换
目录 关键点 技术实现1 技术实现2 摘要: 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式(自动驾驶、人工驾驶、远程驾驶、主动安全),并通过实时消息推送更新车…...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...
Python训练营-Day26-函数专题1:函数定义与参数
题目1:计算圆的面积 任务: 编写一个名为 calculate_circle_area 的函数,该函数接收圆的半径 radius 作为参数,并返回圆的面积。圆的面积 π * radius (可以使用 math.pi 作为 π 的值)要求:函数接收一个位置参数 radi…...
